Merge branch 'unstable-tests'

This commit is contained in:
Evil Factory
2022-04-20 12:56:20 -03:00
1060 changed files with 49349 additions and 41782 deletions

View File

@@ -13,7 +13,7 @@ namespace Barotrauma
private float? defaultZoom;
public float DefaultZoom
{
get { return defaultZoom ?? (GameMain.Config == null || GameMain.Config.EnableMouseLook ? 1.3f : 1.0f); }
get { return defaultZoom ?? (GameSettings.CurrentConfig.EnableMouseLook ? 1.3f : 1.0f); }
set
{
defaultZoom = MathHelper.Clamp(value, 0.5f, 2.0f);
@@ -269,10 +269,10 @@ namespace Barotrauma
if (PlayerInput.KeyDown(Keys.LeftShift)) { moveSpeed *= 2.0f; }
if (PlayerInput.KeyDown(Keys.LeftControl)) { moveSpeed *= 0.5f; }
if (GameMain.Config.KeyBind(InputType.Left).IsDown()) { moveInput.X -= 1.0f; }
if (GameMain.Config.KeyBind(InputType.Right).IsDown()) { moveInput.X += 1.0f; }
if (GameMain.Config.KeyBind(InputType.Down).IsDown()) { moveInput.Y -= 1.0f; }
if (GameMain.Config.KeyBind(InputType.Up).IsDown()) { moveInput.Y += 1.0f; }
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Left].IsDown()) { moveInput.X -= 1.0f; }
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Right].IsDown()) { moveInput.X += 1.0f; }
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Down].IsDown()) { moveInput.Y -= 1.0f; }
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Up].IsDown()) { moveInput.Y += 1.0f; }
}
velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f);
@@ -346,7 +346,7 @@ namespace Barotrauma
float scaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount) * globalZoomScale;
//zoom in further if zoomOutAmount is low and resolution is lower than reference
float newZoom = scaledZoom * (MathHelper.Lerp(0.3f * (1f - Math.Min(globalZoomScale, 1f)), 0f,
(GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(offsetUnscaledLen) : 0.3f) + 1f);
(GameSettings.CurrentConfig.EnableMouseLook) ? (float)Math.Sqrt(offsetUnscaledLen) : 0.3f) + 1f);
Zoom += (newZoom - zoom) / ZoomSmoothness;
}

View File

@@ -37,8 +37,9 @@ namespace Barotrauma
targetPos = attackWorldPos;
}
targetPos.Y = -targetPos.Y;
GUI.DrawLine(spriteBatch, pos, targetPos, GUI.Style.Red * 0.5f, 0, 4);
if (wallTarget != null)
GUI.DrawLine(spriteBatch, pos, targetPos, GUIStyle.Red * 0.5f, 0, 4);
if (wallTarget != null && !IsCoolDownRunning)
{
Vector2 wallTargetPos = wallTarget.Position;
if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; }
@@ -46,19 +47,19 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
}
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity} ({GetTargetMemory(SelectedAiTarget, false)?.Priority.FormatZeroDecimal()})", GUI.Style.Red, Color.Black);
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"({targetValue.FormatZeroDecimal()})", GUI.Style.Red, Color.Black);
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity} ({GetTargetMemory(SelectedAiTarget, false)?.Priority.FormatZeroDecimal()})", GUIStyle.Red, Color.Black);
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"({targetValue.FormatZeroDecimal()})", GUIStyle.Red, Color.Black);
}
/*GUI.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, GUI.Style.Red);
GUI.Font.DrawString(spriteBatch, "updatetargets: " + MathUtils.Round(updateTargetsTimer, 0.1f), pos - Vector2.UnitY * 100.0f, GUI.Style.Red);
GUI.Font.DrawString(spriteBatch, "cooldown: " + MathUtils.Round(coolDownTimer, 0.1f), pos - Vector2.UnitY * 120.0f, GUI.Style.Red);*/
/*GUIStyle.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, GUIStyle.Red);
GUIStyle.Font.DrawString(spriteBatch, "updatetargets: " + MathUtils.Round(updateTargetsTimer, 0.1f), pos - Vector2.UnitY * 100.0f, GUIStyle.Red);
GUIStyle.Font.DrawString(spriteBatch, "cooldown: " + MathUtils.Round(coolDownTimer, 0.1f), pos - Vector2.UnitY * 120.0f, GUIStyle.Red);*/
Color stateColor = Color.White;
switch (State)
{
case AIState.Attack:
stateColor = IsCoolDownRunning ? Color.Orange : GUI.Style.Red;
stateColor = IsCoolDownRunning ? Color.Orange : GUIStyle.Red;
break;
case AIState.Escape:
stateColor = Color.LightBlue;
@@ -78,13 +79,13 @@ namespace Barotrauma
{
GUI.DrawLine(spriteBatch,
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorA.X, -attachJoint.WorldAnchorA.Y)),
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), GUI.Style.Green, 0, 4);
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), GUIStyle.Green, 0, 4);
}
if (LatchOntoAI.AttachPos.HasValue)
{
GUI.DrawLine(spriteBatch, pos,
ConvertUnits.ToDisplayUnits(new Vector2(LatchOntoAI.AttachPos.Value.X, -LatchOntoAI.AttachPos.Value.Y)), GUI.Style.Green, 0, 3);
ConvertUnits.ToDisplayUnits(new Vector2(LatchOntoAI.AttachPos.Value.X, -LatchOntoAI.AttachPos.Value.Y)), GUIStyle.Green, 0, 3);
}
}
@@ -108,12 +109,12 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch,
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y),
new Vector2(previousNode.DrawPosition.X, -previousNode.DrawPosition.Y),
GUI.Style.Red * 0.5f, 0, 3);
GUIStyle.Red * 0.5f, 0, 3);
GUI.SmallFont.DrawString(spriteBatch,
GUIStyle.SmallFont.DrawString(spriteBatch,
currentNode.ID.ToString(),
new Vector2(currentNode.DrawPosition.X - 10, -currentNode.DrawPosition.Y - 30),
GUI.Style.Red);
GUIStyle.Red);
}
}
}
@@ -124,7 +125,7 @@ namespace Barotrauma
Vector2 hitPos = ConvertUnits.ToDisplayUnits(steeringManager.AvoidRayCastHitPosition);
hitPos.Y = -hitPos.Y;
GUI.DrawLine(spriteBatch, hitPos, hitPos + new Vector2(steeringManager.AvoidDir.X, -steeringManager.AvoidDir.Y) * 100, GUI.Style.Red, width: 5);
GUI.DrawLine(spriteBatch, hitPos, hitPos + new Vector2(steeringManager.AvoidDir.X, -steeringManager.AvoidDir.Y) * 100, GUIStyle.Red, width: 5);
//GUI.DrawLine(spriteBatch, pos, ConvertUnits.ToDisplayUnits(steeringManager.AvoidLookAheadPos.X, -steeringManager.AvoidLookAheadPos.Y), Color.Orange, width: 4);
}
}

View File

@@ -18,7 +18,7 @@ namespace Barotrauma
if (SelectedAiTarget?.Entity != null)
{
//GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), GUI.Style.Red);
//GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), GUIStyle.Red);
//GUI.DrawString(spriteBatch, pos + textOffset, $"AI TARGET: {SelectedAiTarget.Entity.ToString()}", Color.White, Color.Black);
}
@@ -87,7 +87,7 @@ namespace Barotrauma
new Vector2(previousNode.DrawPosition.X, -previousNode.DrawPosition.Y),
Color.Blue * 0.5f, 0, 3);
GUI.SmallFont.DrawString(spriteBatch,
GUIStyle.SmallFont.DrawString(spriteBatch,
currentNode.ID.ToString(),
new Vector2(currentNode.DrawPosition.X - 10, -currentNode.DrawPosition.Y - 30),
Color.Blue);

View File

@@ -6,16 +6,16 @@ namespace Barotrauma
{
public static Color ObjectiveIconColor => Color.LightGray;
public static Sprite GetSprite(string identifier, string option, Entity targetEntity)
public static Sprite GetSprite(Identifier identifier, Identifier option, Entity targetEntity)
{
if (string.IsNullOrEmpty(identifier))
if (identifier == Identifier.Empty)
{
return null;
}
identifier = identifier.RemoveWhitespace();
if (Order.Prefabs.TryGetValue(identifier, out Order orderPrefab))
if (OrderPrefab.Prefabs.ContainsKey(identifier))
{
if (!string.IsNullOrEmpty(option) && orderPrefab.OptionSprites.TryGetValue(option, out var optionSprite))
OrderPrefab orderPrefab = OrderPrefab.Prefabs[identifier];
if (option != Identifier.Empty && orderPrefab.OptionSprites.TryGetValue(option, out var optionSprite))
{
return optionSprite;
}
@@ -25,7 +25,7 @@ namespace Barotrauma
}
return orderPrefab.SymbolSprite;
}
return GUI.Style.GetComponentStyle($"{identifier}objectiveicon")?.GetDefaultSprite();
return GUIStyle.GetComponentStyle($"{identifier}objectiveicon")?.GetDefaultSprite();
}
public Sprite GetSprite()

View File

@@ -28,13 +28,13 @@ namespace Barotrauma
{
if (item.Prefab.BrokenSprites.None())
{
Color c = item.prefab.SpriteColor;
Color c = item.Prefab.SpriteColor;
item.SpriteColor = new Color(c.R / 255f * m, c.G / 255f * m, c.B / 255f * m, c.A / 255f);
}
}
foreach (var structure in thalamusStructures)
{
Color c = structure.prefab.SpriteColor;
Color c = structure.Prefab.SpriteColor;
structure.SpriteColor = new Color(c.R / 255f * m, c.G / 255f * m, c.B / 255f * m, c.A / 255f);
}
yield return CoroutineStatus.Running;
@@ -42,7 +42,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
IsAlive = msg.ReadBoolean();
}

View File

@@ -469,7 +469,7 @@ namespace Barotrauma
Color? color = null;
if (character.ExternalHighlight)
{
color = Color.Lerp(Color.White, GUI.Style.Orange, (float)Math.Sin(Timing.TotalTime * 3.5f));
color = Color.Lerp(Color.White, GUIStyle.Orange, (float)Math.Sin(Timing.TotalTime * 3.5f));
}
float depthOffset = GetDepthOffset();
@@ -564,7 +564,7 @@ namespace Barotrauma
Vector2 pos = ConvertUnits.ToDisplayUnits(limb.PullJointWorldAnchorB);
if (currentHull?.Submarine != null) pos += currentHull.Submarine.DrawPosition;
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), GUI.Style.Red, true, 0.01f);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), GUIStyle.Red, true, 0.01f);
pos = ConvertUnits.ToDisplayUnits(limb.PullJointWorldAnchorA);
if (currentHull?.Submarine != null) pos += currentHull.Submarine.DrawPosition;
@@ -575,8 +575,8 @@ namespace Barotrauma
limb.body.DebugDraw(spriteBatch, inWater ? (currentHull == null ? Color.Blue : Color.Cyan) : Color.White);
}
Collider.DebugDraw(spriteBatch, frozen ? GUI.Style.Red : (inWater ? Color.SkyBlue : Color.Gray));
GUI.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.FormatSingleDecimal(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange);
Collider.DebugDraw(spriteBatch, frozen ? GUIStyle.Red : (inWater ? Color.SkyBlue : Color.Gray));
GUIStyle.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.FormatSingleDecimal(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange);
foreach (var joint in LimbJoints)
{
@@ -607,10 +607,10 @@ namespace Barotrauma
{
Vector2 pos = ConvertUnits.ToDisplayUnits(humanoid.RightHandIKPos);
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.DrawPosition; }
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUIStyle.Green, true);
pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos);
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.DrawPosition; }
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUIStyle.Green, true);
Vector2 aimPos = humanoid.AimSourceWorldPos;
aimPos.Y = -aimPos.Y;

View File

@@ -6,22 +6,22 @@ namespace Barotrauma
{
partial class Attack
{
[Serialize("StructureBlunt", true), Editable()]
[Serialize("StructureBlunt", IsPropertySaveable.Yes), Editable()]
public string StructureSoundType { get; private set; }
private RoundSound sound;
private ParticleEmitter particleEmitter;
partial void InitProjSpecific(XElement element)
partial void InitProjSpecific(ContentXElement element)
{
if (element.Attribute("sound") != null)
if (element.GetAttribute("sound") != null)
{
DebugConsole.ThrowError("Error in attack ("+element+") - sounds should be defined as child elements, not as attributes.");
return;
}
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -29,7 +29,7 @@ namespace Barotrauma
particleEmitter = new ParticleEmitter(subElement);
break;
case "sound":
sound = Submarine.LoadRoundSound(subElement);
sound = RoundSound.Load(subElement);
break;
}

View File

@@ -131,7 +131,7 @@ namespace Barotrauma
private class GUIMessage
{
public string RawText;
public string Identifier;
public Identifier Identifier;
public string Text;
private int _value;
@@ -142,7 +142,7 @@ namespace Barotrauma
{
_value = value;
Text = RawText.Replace("[value]", _value.ToString());
Size = GUI.Font.MeasureString(Text);
Size = GUIStyle.Font.MeasureString(Text);
}
}
@@ -154,7 +154,7 @@ namespace Barotrauma
public bool PlaySound;
public GUIMessage(string rawText, Color color, float delay, string identifier = null, int? value = null, float lifeTime = 3.0f)
public GUIMessage(string rawText, Color color, float delay, Identifier identifier = default, int? value = null, float lifeTime = 3.0f)
{
RawText = Text = rawText;
if (value.HasValue)
@@ -163,7 +163,7 @@ namespace Barotrauma
Value = value.Value;
}
Timer = -delay;
Size = GUI.Font.MeasureString(Text);
Size = GUIStyle.Font.MeasureString(Text);
Color = color;
Identifier = identifier;
Lifetime = lifeTime;
@@ -202,14 +202,14 @@ namespace Barotrauma
get { return activeObjectiveEntities; }
}
partial void InitProjSpecific(XElement mainElement)
partial void InitProjSpecific(ContentXElement mainElement)
{
soundTimer = Rand.Range(0.0f, Params.SoundInterval);
sounds = new List<CharacterSound>();
Params.Sounds.ForEach(s => sounds.Add(new CharacterSound(s)));
foreach (XElement subElement in mainElement.Elements())
foreach (var subElement in mainElement.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -267,11 +267,11 @@ namespace Barotrauma
//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 (GameMain.Config.KeyBind(InputType.Shoot).Equals(GameMain.Config.KeyBind(InputType.Select)))
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Shoot] == GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select])
{
keys[(int)InputType.Select].Reset();
}
if (GameMain.Config.KeyBind(InputType.Shoot).Equals(GameMain.Config.KeyBind(InputType.Use)))
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Shoot] == GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Use])
{
keys[(int)InputType.Use].Reset();
}
@@ -331,7 +331,7 @@ namespace Barotrauma
Position +
PlayerInput.MouseSpeed.ClampLength(10.0f); //apply a little bit of movement to the cursor pos to prevent AFK kicking
}
else if (!GameMain.Config.EnableMouseLook)
else if (!GameSettings.CurrentConfig.EnableMouseLook)
{
cam.OffsetAmount = targetOffsetAmount = 0.0f;
}
@@ -446,15 +446,15 @@ namespace Barotrauma
if (GameMain.NetworkMember != null && controlled == this)
{
string chatMessage = CauseOfDeath.Type == CauseOfDeathType.Affliction ?
LocalizedString chatMessage = CauseOfDeath.Type == CauseOfDeathType.Affliction ?
CauseOfDeath.Affliction.SelfCauseOfDeathDescription :
TextManager.Get("Self_CauseOfDeathDescription." + CauseOfDeath.Type.ToString(), fallBackTag: "Self_CauseOfDeathDescription.Damage");
TextManager.Get("Self_CauseOfDeathDescription." + CauseOfDeath.Type.ToString(), "Self_CauseOfDeathDescription.Damage");
if (GameMain.Client != null) { chatMessage += " " + TextManager.Get("DeathChatNotification"); }
GameMain.NetworkMember.RespawnManager?.ShowRespawnPromptIfNeeded();
GameMain.NetworkMember.AddChatMessage(chatMessage, ChatMessageType.Dead);
GameMain.NetworkMember.AddChatMessage(chatMessage.Value, ChatMessageType.Dead);
GameMain.LightManager.LosEnabled = false;
controlled = null;
if (!(Screen.Selected?.Cam is null))
@@ -726,9 +726,9 @@ namespace Barotrauma
}
}
partial void SetOrderProjSpecific(Order order, string orderOption, int priority)
partial void SetOrderProjSpecific(Order order)
{
GameMain.GameSession?.CrewManager?.AddCurrentOrderIcon(this, order, orderOption, priority);
GameMain.GameSession?.CrewManager?.AddCurrentOrderIcon(this, order);
}
public static void AddAllToGUIUpdateList()
@@ -812,7 +812,7 @@ namespace Barotrauma
Controlled != this &&
Submarine != null &&
Controlled.Submarine == Submarine &&
GameMain.Config.LosMode != LosMode.None)
GameSettings.CurrentConfig.Graphics.LosMode != LosMode.None)
{
float yPos = Controlled.AnimController.FloorY - 1.5f;
@@ -854,7 +854,7 @@ namespace Barotrauma
if (speechBubbleTimer > 0.0f)
{
GUI.SpeechBubbleIcon.Draw(spriteBatch, pos - Vector2.UnitY * 5,
GUIStyle.SpeechBubbleIcon.Value.Sprite.Draw(spriteBatch, pos - Vector2.UnitY * 5,
speechBubbleColor * Math.Min(speechBubbleTimer, 1.0f), 0.0f,
Math.Min(speechBubbleTimer, 1.0f));
}
@@ -880,7 +880,7 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch,
cursorPos,
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y),
ToolBox.GradientLerp(dist, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green), width: 2);
ToolBox.GradientLerp(dist, GUIStyle.Red, GUIStyle.Orange, GUIStyle.Green), width: 2);
}
}
return;
@@ -899,10 +899,10 @@ namespace Barotrauma
{
if (info != null)
{
string name = Info.DisplayName;
LocalizedString name = Info.DisplayName;
if (controlled == null && name != Info.Name) { name += " " + TextManager.Get("Disguised"); }
Vector2 nameSize = GUI.Font.MeasureString(name);
Vector2 nameSize = GUIStyle.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
Color nameColor = GetNameColor();
@@ -916,7 +916,7 @@ namespace Barotrauma
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
{
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
var iconStyle = GUIStyle.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
@@ -929,11 +929,11 @@ namespace Barotrauma
}
}
GUI.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
GUI.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
GUIStyle.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
GUIStyle.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
if (GameMain.DebugDraw)
{
GUI.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
GUIStyle.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
}
}
@@ -941,7 +941,7 @@ namespace Barotrauma
if (petBehavior != null && !IsDead && !IsUnconscious)
{
var petStatus = petBehavior.GetCurrentStatusIndicatorType();
var iconStyle = GUI.Style.GetComponentStyle("PetIcon." + petStatus);
var iconStyle = GUIStyle.GetComponentStyle("PetIcon." + petStatus);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
@@ -963,7 +963,7 @@ namespace Barotrauma
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f),
CharacterHealth.DisplayedVitality / MaxVitality,
Color.Lerp(GUI.Style.Red, GUI.Style.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);
}
}
@@ -987,7 +987,7 @@ namespace Barotrauma
}
}
Color nameColor = GUI.Style.TextColor;
Color nameColor = GUIStyle.TextColorNormal;
if (Controlled != null && team != Controlled.TeamID)
{
if (TeamID == CharacterTeamType.FriendlyNPC)
@@ -996,13 +996,13 @@ namespace Barotrauma
}
else
{
nameColor = GUI.Style.Red;
nameColor = GUIStyle.Red;
}
}
return nameColor;
}
public void AddMessage(string rawText, Color color, bool playSound, string identifier = null, int? value = null, float lifetime = 3.0f)
public void AddMessage(string rawText, Color color, bool playSound, Identifier identifier = default, int? value = null, float lifetime = 3.0f)
{
GUIMessage existingMessage = null;
@@ -1089,12 +1089,12 @@ namespace Barotrauma
matchingSounds.Clear();
foreach (var s in sounds)
{
if (s.Type == soundType && (s.Gender == Gender.None || (info != null && info.Gender == s.Gender)))
if (s.Type == soundType && (s.TagSet.None() || (info != null && s.TagSet.IsSubsetOf(info.Head.Preset.TagSet))))
{
matchingSounds.Add(s);
}
}
var selectedSound = matchingSounds.GetRandom();
var selectedSound = matchingSounds.GetRandomUnsynced();
if (selectedSound?.Sound == null) { return; }
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: CurrentHull, ignoreMuffling: selectedSound.IgnoreMuffling);
soundTimer = Params.SoundInterval;
@@ -1117,7 +1117,7 @@ namespace Barotrauma
/// <summary>
/// Note that when a predicate is provided, the random option uses Linq.Where() extension method, which creates a new collection.
/// </summary>
public CharacterSound GetSound(Func<CharacterSound, bool> predicate = null, bool random = false) => random ? sounds.GetRandom(predicate) : sounds.FirstOrDefault(predicate);
public CharacterSound GetSound(Func<CharacterSound, bool> predicate = null, bool random = false) => random ? sounds.GetRandomUnsynced(predicate) : sounds.FirstOrDefault(predicate);
partial void ImplodeFX()
{
@@ -1151,19 +1151,11 @@ namespace Barotrauma
}
}
partial void OnMoneyChanged(int prevAmount, int newAmount)
{
if (newAmount > prevAmount)
{
int increase = newAmount - prevAmount;
AddMessage("+" + TextManager.GetWithVariable("currencyformat", "[credits]", "[value]"),
GUI.Style.Yellow, playSound: this == Controlled, "money", increase);
}
}
partial void OnMoneyChanged(int prevAmount, int newAmount) { }
partial void OnTalentGiven(TalentPrefab talentPrefab)
{
AddMessage(TextManager.Get("talentname." + talentPrefab.Identifier), GUI.Style.Yellow, playSound: this == Controlled);
AddMessage(TextManager.Get("talentname." + talentPrefab.Identifier).Value, GUIStyle.Yellow, playSound: this == Controlled);
}
}
}

View File

@@ -35,23 +35,23 @@ namespace Barotrauma
MinSize = new Point(100, 50),
RelativeOffset = new Vector2(0.0f, 0.01f)
}, isHorizontal: false, childAnchor: Anchor.TopCenter);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), TopContainer.RectTransform), character.DisplayName, textAlignment: Alignment.Center, textColor: GUI.Style.Red);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), TopContainer.RectTransform), character.DisplayName, textAlignment: Alignment.Center, textColor: GUIStyle.Red);
TopHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.6f), TopContainer.RectTransform)
{
MinSize = new Point(100, HUDLayoutSettings.HealthBarArea.Size.Y)
}, barSize: 0.0f, style: "CharacterHealthBarCentered")
{
Color = GUI.Style.Red
Color = GUIStyle.Red
};
SideContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), bossHealthContainer.RectTransform)
{
MinSize = new Point(80, 60)
}, isHorizontal: false, childAnchor: Anchor.TopRight);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), SideContainer.RectTransform), character.DisplayName, textAlignment: Alignment.CenterRight, textColor: GUI.Style.Red);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), SideContainer.RectTransform), character.DisplayName, textAlignment: Alignment.CenterRight, textColor: GUIStyle.Red);
SideHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.7f), SideContainer.RectTransform), barSize: 0.0f, style: "CharacterHealthBar")
{
Color = GUI.Style.Red
Color = GUIStyle.Red
};
TopContainer.Visible = SideContainer.Visible = false;
@@ -72,7 +72,7 @@ namespace Barotrauma
private static readonly List<BossHealthBar> bossHealthBars = new List<BossHealthBar>();
private static readonly Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>();
private static readonly Dictionary<Identifier, LocalizedString> cachedHudTexts = new Dictionary<Identifier, LocalizedString>();
private static GUILayoutGroup bossHealthContainer;
@@ -119,14 +119,12 @@ namespace Barotrauma
!ConversationAction.FadeScreenToBlack;
}
public static string GetCachedHudText(string textTag, string keyBind)
public static LocalizedString GetCachedHudText(string textTag, InputType keyBind)
{
if (cachedHudTexts.TryGetValue(textTag + keyBind, out string text))
{
return text;
}
text = TextManager.GetWithVariable(textTag, "[key]", keyBind);
cachedHudTexts.Add(textTag + keyBind, text);
Identifier key = (textTag + keyBind).ToIdentifier();
if (cachedHudTexts.TryGetValue(key, out LocalizedString text)) { return text; }
text = TextManager.GetWithVariable(textTag, "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(keyBind)).Value;
cachedHudTexts.Add(key, text);
return text;
}
@@ -256,24 +254,24 @@ namespace Barotrauma
if (GameMain.GameSession?.CrewManager != null)
{
orderIndicatorCount.Clear();
foreach (Pair<Order, float?> activeOrder in GameMain.GameSession.CrewManager.ActiveOrders)
foreach (CrewManager.ActiveOrder activeOrder in GameMain.GameSession.CrewManager.ActiveOrders)
{
if (!DrawIcon(activeOrder.First)) { continue; }
if (!DrawIcon(activeOrder.Order)) { continue; }
if (activeOrder.Second.HasValue)
if (activeOrder.FadeOutTime.HasValue)
{
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First, iconAlpha: MathHelper.Clamp(activeOrder.Second.Value / 10.0f, 0.2f, 1.0f));
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.Order, iconAlpha: MathHelper.Clamp(activeOrder.FadeOutTime.Value / 10.0f, 0.2f, 1.0f));
}
else
{
float iconAlpha = GetDistanceBasedIconAlpha(activeOrder.First.TargetSpatialEntity, maxDistance: 450.0f);
float iconAlpha = GetDistanceBasedIconAlpha(activeOrder.Order.TargetSpatialEntity, maxDistance: 450.0f);
if (iconAlpha <= 0.0f) { continue; }
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First,
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.Order,
iconAlpha: iconAlpha, createOffset: false, scaleMultiplier: 0.5f, overrideAlpha: true);
}
}
if (character.GetCurrentOrderWithTopPriority()?.Order is Order currentOrder && DrawIcon(currentOrder))
if (character.GetCurrentOrderWithTopPriority() is Order currentOrder && DrawIcon(currentOrder))
{
DrawOrderIndicator(spriteBatch, cam, character, currentOrder, 1.0f);
}
@@ -310,8 +308,8 @@ namespace Barotrauma
if (!brokenItem.IsInteractable(character)) { continue; }
float alpha = GetDistanceBasedIconAlpha(brokenItem);
if (alpha <= 0.0f) continue;
GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUI.BrokenIcon,
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUIStyle.BrokenIcon.Value.Sprite,
Color.Lerp(GUIStyle.Red, GUIStyle.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
}
float GetDistanceBasedIconAlpha(ISpatialEntity target, float maxDistance = 1000.0f)
@@ -344,12 +342,12 @@ namespace Barotrauma
circleSize = MathHelper.Clamp(circleSize, 45.0f, 100.0f) * Math.Min((focusedItemOverlayTimer - 1.0f) * 5.0f, 1.0f);
if (circleSize > 0.0f)
{
Vector2 scale = new Vector2(circleSize / GUI.Style.FocusIndicator.FrameSize.X);
GUI.Style.FocusIndicator.Draw(spriteBatch,
(int)((focusedItemOverlayTimer - 1.0f) * GUI.Style.FocusIndicator.FrameCount * 3.0f),
Vector2 scale = new Vector2(circleSize / GUIStyle.FocusIndicator.FrameSize.X);
GUIStyle.FocusIndicator.Draw(spriteBatch,
(int)((focusedItemOverlayTimer - 1.0f) * GUIStyle.FocusIndicator.FrameCount * 3.0f),
circlePos,
Color.LightBlue * 0.3f,
origin: GUI.Style.FocusIndicator.FrameSize.ToVector2() / 2,
origin: GUIStyle.FocusIndicator.FrameSize.ToVector2() / 2,
rotate: (float)Timing.TotalTime,
scale: scale);
}
@@ -367,8 +365,8 @@ namespace Barotrauma
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
Vector2 textSize = GUI.Font.MeasureString(hudTexts.First().Text);
Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(hudTexts.First().Text);
Vector2 textSize = GUIStyle.Font.MeasureString(hudTexts.First().Text);
Vector2 largeTextSize = GUIStyle.SubHeadingFont.MeasureString(hudTexts.First().Text);
Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition);
startPos.Y -= (hudTexts.Count + 1) * textSize.Y;
@@ -383,14 +381,14 @@ namespace Barotrauma
float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f);
GUI.DrawString(spriteBatch, textPos, hudTexts.First().Text, hudTexts.First().Color * alpha, Color.Black * alpha * 0.7f, 2, font: GUI.SubHeadingFont);
GUI.DrawString(spriteBatch, textPos, hudTexts.First().Text, hudTexts.First().Color * alpha, Color.Black * alpha * 0.7f, 2, font: GUIStyle.SubHeadingFont, ForceUpperCase.No);
startPos.X += dir * 10.0f * GUI.Scale;
textPos.X += dir * 10.0f * GUI.Scale;
textPos.Y += largeTextSize.Y;
foreach (ColoredText coloredText in hudTexts.Skip(1))
{
if (dir == -1) textPos.X = (int)(startPos.X - GUI.SmallFont.MeasureString(coloredText.Text).X);
GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont);
if (dir == -1) textPos.X = (int)(startPos.X - GUIStyle.SmallFont.MeasureString(coloredText.Text).X);
GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUIStyle.SmallFont);
textPos.Y += textSize.Y;
}
}
@@ -405,7 +403,7 @@ namespace Barotrauma
{
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None || npc.Submarine != character.Submarine || npc.IsDead || npc.IsIncapacitated) { continue; }
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + npc.CampaignInteractionType);
var iconStyle = GUIStyle.GetComponentStyle("CampaignInteractionIcon." + npc.CampaignInteractionType);
if (iconStyle == null) { continue; }
Range<float> visibleRange = new Range<float>(npc.CurrentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, float.PositiveInfinity);
if (npc.CampaignInteractionType == CampaignMode.InteractionType.Examine)
@@ -486,12 +484,14 @@ namespace Barotrauma
(int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f),
(int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2),
(int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f)), character.Info.IsDisguisedAsAnother);
character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true, character.Info.IsDisguisedAsAnother);
float yOffset = (GameMain.GameSession?.Campaign is MultiPlayerCampaign ? -10 : 4) * GUI.Scale;
character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, yOffset), targetWidth: HUDLayoutSettings.PortraitArea.Width, true, character.Info.IsDisguisedAsAnother);
character.Info.DrawForeground(spriteBatch);
}
mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud();
if (mouseOnPortrait)
{
GUI.UIGlow.Draw(spriteBatch, HUDLayoutSettings.BottomRightInfoArea, GUI.Style.Green * 0.5f);
GUIStyle.UIGlow.Draw(spriteBatch, HUDLayoutSettings.BottomRightInfoArea, GUIStyle.Green * 0.5f);
}
}
if (ShouldDrawInventory(character))
@@ -555,28 +555,28 @@ namespace Barotrauma
string focusName = character.FocusedCharacter.Info == null ? character.FocusedCharacter.DisplayName : character.FocusedCharacter.Info.DisplayName;
Vector2 textPos = startPos;
Vector2 textSize = GUI.Font.MeasureString(focusName);
Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusName);
Vector2 textSize = GUIStyle.Font.MeasureString(focusName);
Vector2 largeTextSize = GUIStyle.SubHeadingFont.MeasureString(focusName);
textPos -= new Vector2(textSize.X / 2, textSize.Y);
Color nameColor = character.FocusedCharacter.GetNameColor();
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont);
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
textPos.X += 10.0f * GUI.Scale;
textPos.Y += GUI.SubHeadingFont.MeasureString(focusName).Y;
textPos.Y += GUIStyle.SubHeadingFont.MeasureString(focusName).Y;
if (!character.FocusedCharacter.IsIncapacitated && character.FocusedCharacter.IsPet)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("PlayHint", GameMain.Config.KeyBindText(InputType.Use)),
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("PlayHint", InputType.Use),
GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
textPos.Y += largeTextSize.Y;
}
if (character.FocusedCharacter.CanBeDragged)
{
string text = character.CanEat ? "EatHint" : "GrabHint";
GUI.DrawString(spriteBatch, textPos, GetCachedHudText(text, GameMain.Config.KeyBindText(InputType.Grab)),
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
GUI.DrawString(spriteBatch, textPos, GetCachedHudText(text, InputType.Grab),
GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
textPos.Y += largeTextSize.Y;
}
@@ -585,13 +585,13 @@ namespace Barotrauma
character.FocusedCharacter.CharacterHealth.UseHealthWindow &&
character.CanInteractWith(character.FocusedCharacter, 160f, false))
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", GameMain.Config.KeyBindText(InputType.Health)),
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", InputType.Health),
GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
textPos.Y += textSize.Y;
}
if (!string.IsNullOrEmpty(character.FocusedCharacter.customInteractHUDText) && character.FocusedCharacter.AllowCustomInteract)
if (!character.FocusedCharacter.CustomInteractHUDText.IsNullOrEmpty() && character.FocusedCharacter.AllowCustomInteract)
{
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.customInteractHUDText, GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.CustomInteractHUDText, GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
textPos.Y += textSize.Y;
}
}

View File

@@ -8,6 +8,7 @@ using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
using Barotrauma.IO;
using Barotrauma.Items.Components;
using System.Collections.Immutable;
namespace Barotrauma
{
@@ -34,17 +35,17 @@ namespace Barotrauma
public static void Init()
{
infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.GetDefaultSprite();
infoAreaPortraitBG = GUIStyle.GetComponentStyle("InfoAreaPortraitBG")?.GetDefaultSprite();
new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(833, 298, 142, 98), null, 0);
}
partial void LoadHeadSpriteProjectSpecific(XElement limbElement)
partial void LoadHeadSpriteProjectSpecific(ContentXElement limbElement)
{
XElement maskElement = limbElement.Element("tintmask");
ContentXElement maskElement = limbElement.GetChildElement("tintmask");
if (maskElement != null)
{
string tintMaskPath = maskElement.GetAttributeString("texture", "");
if (!string.IsNullOrWhiteSpace(tintMaskPath))
ContentPath tintMaskPath = maskElement.GetAttributeContentPath("texture");
if (!tintMaskPath.IsNullOrEmpty())
{
tintMask = new Sprite(maskElement, file: Limb.GetSpritePath(tintMaskPath, this));
tintHighlightThreshold = maskElement.GetAttributeFloat("highlightthreshold", 0.6f);
@@ -66,7 +67,7 @@ namespace Barotrauma
new GUICustomComponent(new RectTransform(new Vector2(0.425f, 1.0f), headerArea.RectTransform),
onDraw: (sb, component) => DrawInfoFrameCharacterIcon(sb, component.Rect));
ScalableFont font = paddedFrame.Rect.Width < 280 ? GUI.SmallFont : GUI.Font;
GUIFont font = paddedFrame.Rect.Width < 280 ? GUIStyle.SmallFont : GUIStyle.Font;
var headerTextArea = new GUILayoutGroup(new RectTransform(new Vector2(0.575f, 1.0f), headerArea.RectTransform))
{
@@ -77,9 +78,9 @@ namespace Barotrauma
Color? nameColor = null;
if (Job != null) { nameColor = Job.Prefab.UIColor; }
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), ToolBox.LimitString(Name, GUI.Font, headerTextArea.Rect.Width), textColor: nameColor, font: GUI.Font)
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), ToolBox.LimitString(Name, GUIStyle.Font, headerTextArea.Rect.Width), textColor: nameColor, font: GUIStyle.Font)
{
ForceUpperCase = true,
ForceUpperCase = ForceUpperCase.Yes,
Padding = Vector4.Zero
};
@@ -98,9 +99,11 @@ namespace Barotrauma
};
}
if (personalityTrait != null)
if (PersonalityTrait != null)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + personalityTrait.Name.Replace(" ", ""))), font: font)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + PersonalityTrait.Name.Replace(" ".ToIdentifier(), "".ToIdentifier()))),
font: font)
{
Padding = Vector4.Zero
};
@@ -148,10 +151,11 @@ namespace Barotrauma
Stretch = true
};
string deadDescription = TextManager.AddPunctuation(':', TextManager.Get("deceased") + "\n" + Character.CauseOfDeath.Affliction?.CauseOfDeathDescription ??
TextManager.AddPunctuation(':', TextManager.Get("CauseOfDeath"), TextManager.Get("CauseOfDeath." + Character.CauseOfDeath.Type.ToString())));
LocalizedString deadDescription =
TextManager.Get("deceased") + "\n" +
(Character.CauseOfDeath.Affliction?.CauseOfDeathDescription ?? TextManager.Get("CauseOfDeath." + Character.CauseOfDeath.Type.ToString()));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), deadArea.RectTransform), deadDescription, textColor: GUI.Style.Red, font: font, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), deadArea.RectTransform), deadDescription, textColor: GUIStyle.Red, font: font, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
}
if (returnParent)
@@ -182,13 +186,13 @@ namespace Barotrauma
Color? textColor = null;
if (Job != null) { textColor = Job.Prefab.UIColor; }
GUITextBlock textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(40, 0) }, text, textColor: textColor, font: GUI.SmallFont);
GUITextBlock textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(40, 0) }, text, textColor: textColor, font: GUIStyle.SmallFont);
new GUICustomComponent(new RectTransform(new Point(frame.Rect.Height, frame.Rect.Height), frame.RectTransform, Anchor.CenterLeft) { IsFixedSize = false },
onDraw: (sb, component) => DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()));
return frame;
}
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel)
partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel)
{
if (TeamID == CharacterTeamType.FriendlyNPC) { return; }
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
@@ -199,9 +203,10 @@ namespace Barotrauma
if ((int)newLevel > (int)prevLevel)
{
int increase = Math.Max((int)newLevel - (int)prevLevel, 1);
Character?.AddMessage(
"+[value] "+ TextManager.Get("SkillName." + skillIdentifier),
specialIncrease ? GUI.Style.Orange : GUI.Style.Green,
"+[value] "+ TextManager.Get("SkillName." + skillIdentifier).Value,
specialIncrease ? GUIStyle.Orange : GUIStyle.Green,
playSound: Character == Character.Controlled, skillIdentifier, increase);
}
}
@@ -216,8 +221,8 @@ namespace Barotrauma
{
int increase = newAmount - prevAmount;
Character?.AddMessage(
"+[value] " + TextManager.Get("experienceshort"),
GUI.Style.Blue, playSound: Character == Character.Controlled, "exp", increase);
"+[value] " + TextManager.Get("experienceshort").Value,
GUIStyle.Blue, playSound: Character == Character.Controlled, "exp".ToIdentifier(), increase);
}
}
@@ -227,9 +232,13 @@ namespace Barotrauma
if (idCard.StoredOwnerAppearance.JobPrefab == null || idCard.StoredOwnerAppearance.Portrait == null)
{
string[] readTags = idCard.Item.Tags.Split(',');
var readTags = idCard.Item.Tags.Split(',')
.Where(s => s.Contains(':'))
.Select(s => s.Split(':'))
.Select(s => (s[0].ToIdentifier(),s[1]))
.ToImmutableDictionary();
if (readTags.Length == 0) { return; }
if (readTags.None()) { return; }
if (idCard.StoredOwnerAppearance.JobPrefab == null)
{
@@ -238,7 +247,7 @@ namespace Barotrauma
if (idCard.StoredOwnerAppearance.Portrait == null)
{
idCard.StoredOwnerAppearance.ExtractAppearance(this, readTags);
idCard.StoredOwnerAppearance.ExtractAppearance(this, idCard);
}
}
@@ -267,17 +276,17 @@ namespace Barotrauma
{
LoadHeadAttachments();
}
FaceAttachment?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.FaceAttachment)));
BeardElement?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Beard)));
MoustacheElement?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Moustache)));
HairElement?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Hair)));
Head.FaceAttachment?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.FaceAttachment)));
Head.BeardElement?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Beard)));
Head.MoustacheElement?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Moustache)));
Head.HairElement?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Hair)));
if (omitJob)
{
JobPrefab.NoJobElement?.Element("PortraitClothing")?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.JobIndicator)));
JobPrefab.NoJobElement?.GetChildElement("PortraitClothing")?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
else
{
Job?.Prefab.ClothingElement?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.JobIndicator)));
Job?.Prefab.ClothingElement?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
}
@@ -288,7 +297,7 @@ namespace Barotrauma
{
if (sprite == null) { return; }
if (Head.SheetIndex == null) { return; }
Point location = CalculateOffset(sprite, Head.SheetIndex.Value.ToPoint());
Point location = CalculateOffset(sprite, Head.SheetIndex.ToPoint());
sprite.SourceRect = new Rectangle(location, sprite.SourceRect.Size);
}
@@ -301,6 +310,32 @@ namespace Barotrauma
HUDLayoutSettings.BottomRightInfoArea.Height / (float)infoAreaPortraitBG.SourceRect.Height));
}
public void DrawForeground(SpriteBatch spriteBatch)
{
if (Character is null || !(GameMain.GameSession?.Campaign is MultiPlayerCampaign)) { return; }
const int million = 1000000;
int xfraction = (int)(HUDLayoutSettings.BottomRightInfoArea.Width * 0.2f);
int yoffset = GUI.IntScale(6);
int walletAmount = Character.Wallet.Balance;
LocalizedString str = walletAmount >= million ? TextManager.Get("crewwallet.balance.toomuchtoshow") : TextManager.FormatCurrency(walletAmount);
Vector2 size = GUIStyle.Font.MeasureString(str);
int barHeight = GUI.IntScale(18);
Rectangle barRect = new Rectangle((int)(HUDLayoutSettings.BottomRightInfoArea.X + xfraction / 2.5f), HUDLayoutSettings.BottomRightInfoArea.Bottom - barHeight - yoffset, HUDLayoutSettings.BottomRightInfoArea.Width - xfraction, barHeight);
float textScale = Math.Max(0.1f, Math.Min(barRect.Width / size.X, barRect.Height / size.Y)) - 0.01f;
GUIStyle.WalletPortraitBG.Draw(spriteBatch, barRect, Color.White);
int iconSize = GUI.IntScale(28);
int iconXOffset = iconSize / 2;
Rectangle iconRect = new Rectangle(barRect.Right - iconXOffset, barRect.Top - iconSize / 4, iconSize, iconSize);
GUIStyle.CrewWalletIconSmall.Draw(spriteBatch, iconRect, Color.White);
var (scaledTextSizeX, scaledTextSizeY) = size * textScale;
GUIStyle.Font.DrawString(spriteBatch, str, new Vector2(barRect.Right - iconXOffset - scaledTextSizeX - GUI.IntScale(4), barRect.Center.Y - scaledTextSizeY / 2), GUIStyle.TextColorNormal, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f);
}
public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false, bool evaluateDisguise = false)
{
if (evaluateDisguise && IsDisguised) { return; }
@@ -414,19 +449,16 @@ namespace Barotrauma
{
var currEffect = spriteBatch.GetCurrentEffect();
float scale = Math.Min(targetAreaSize.X / headSprite.size.X, targetAreaSize.Y / headSprite.size.Y);
if (Head.SheetIndex.HasValue)
{
headSprite.SourceRect = new Rectangle(CalculateOffset(headSprite, Head.SheetIndex.Value.ToPoint()), headSprite.SourceRect.Size);
}
headSprite.SourceRect = new Rectangle(CalculateOffset(headSprite, Head.SheetIndex.ToPoint()), headSprite.SourceRect.Size);
SetHeadEffect(spriteBatch);
headSprite.Draw(spriteBatch, screenPos, scale: scale, color: SkinColor);
headSprite.Draw(spriteBatch, screenPos, scale: scale, color: Head.SkinColor);
if (AttachmentSprites != null)
{
float depthStep = 0.000001f;
foreach (var attachment in AttachmentSprites)
{
SetAttachmentEffect(spriteBatch, attachment);
DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep, GetAttachmentColor(attachment, HairColor, FacialHairColor));
DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep, GetAttachmentColor(attachment, Head.HairColor, Head.FacialHairColor));
depthStep += depthStep;
}
}
@@ -479,14 +511,17 @@ namespace Barotrauma
attachment.Sprite.Draw(spriteBatch, drawPos, color ?? Color.White, origin, rotate: 0, scale: scale, depth: depth, spriteEffect: spriteEffects);
}
public static CharacterInfo ClientRead(string speciesName, IReadMessage inc)
public static CharacterInfo ClientRead(Identifier speciesName, IReadMessage inc)
{
ushort infoID = inc.ReadUInt16();
string newName = inc.ReadString();
string originalName = inc.ReadString();
int gender = inc.ReadByte();
int race = inc.ReadByte();
int headSpriteID = inc.ReadByte();
int tagCount = inc.ReadByte();
HashSet<Identifier> tagSet = new HashSet<Identifier>();
for (int i = 0; i < tagCount; i++)
{
tagSet.Add(inc.ReadIdentifier());
}
int hairIndex = inc.ReadByte();
int beardIndex = inc.ReadByte();
int moustacheIndex = inc.ReadByte();
@@ -500,14 +535,14 @@ namespace Barotrauma
int variant = inc.ReadByte();
JobPrefab jobPrefab = null;
Dictionary<string, float> skillLevels = new Dictionary<string, float>();
Dictionary<Identifier, float> skillLevels = new Dictionary<Identifier, float>();
if (!string.IsNullOrEmpty(jobIdentifier))
{
jobPrefab = JobPrefab.Get(jobIdentifier);
byte skillCount = inc.ReadByte();
for (int i = 0; i < skillCount; i++)
{
string skillIdentifier = inc.ReadString();
Identifier skillIdentifier = inc.ReadIdentifier();
float skillLevel = inc.ReadSingle();
skillLevels.Add(skillIdentifier, skillLevel);
}
@@ -518,14 +553,14 @@ namespace Barotrauma
{
ID = infoID,
};
ch.RecreateHead(headSpriteID,(Race)race, (Gender)gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
ch.SkinColor = skinColor;
ch.HairColor = hairColor;
ch.FacialHairColor = facialHairColor;
ch.RecreateHead(tagSet.ToImmutableHashSet(), hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
ch.Head.SkinColor = skinColor;
ch.Head.HairColor = hairColor;
ch.Head.FacialHairColor = facialHairColor;
ch.SetPersonalityTrait();
if (ch.Job != null)
{
foreach (KeyValuePair<string, float> skill in skillLevels)
foreach (KeyValuePair<Identifier, float> skill in skillLevels)
{
Skill matchingSkill = ch.Job.Skills.Find(s => s.Identifier == skill.Key);
if (matchingSkill == null)
@@ -538,17 +573,8 @@ namespace Barotrauma
ch.Job.Skills.RemoveAll(s => !skillLevels.ContainsKey(s.Identifier));
}
byte savedStatValueCount = inc.ReadByte();
for (int i = 0; i < savedStatValueCount; i++)
{
int statType = inc.ReadByte();
string statIdentifier = inc.ReadString();
float statValue = inc.ReadSingle();
bool removeOnDeath = inc.ReadBoolean();
ch.ChangeSavedStatValue((StatTypes)statType, statValue, statIdentifier, removeOnDeath);
}
ch.ExperiencePoints = inc.ReadUInt16();
ch.AdditionalTalentPoints = inc.ReadUInt16();
ch.AdditionalTalentPoints = inc.ReadRangedInteger(0, MaxAdditionalTalentPoints);
return ch;
}
@@ -605,12 +631,12 @@ namespace Barotrauma
{ RelativeOffset = new Vector2(-0.01f, 0.0f) });
}
RectTransform createItemRectTransform(string labelTag, float width = 0.6f)
RectTransform createItemRectTransform(Identifier labelTag, float width = 0.6f)
{
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.166f), content.RectTransform));
var label = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), layoutGroup.RectTransform),
TextManager.Get(labelTag), font: GUI.SubHeadingFont);
TextManager.Get(labelTag), font: GUIStyle.SubHeadingFont);
var bottomItem = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), layoutGroup.RectTransform),
style: null);
@@ -618,43 +644,40 @@ namespace Barotrauma
return new RectTransform(new Vector2(width, 1.0f), bottomItem.RectTransform, Anchor.Center);
}
RectTransform genderItemRT = createItemRectTransform("Gender", 1.0f);
RectTransform menuCategoryRT = createItemRectTransform(info.Prefab.MenuCategoryVar, 1.0f);
GUILayoutGroup genderContainer =
new GUILayoutGroup(genderItemRT, isHorizontal: true)
GUILayoutGroup menuCategoryContainer =
new GUILayoutGroup(menuCategoryRT, isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
void createGenderButton(Gender gender)
void createMenuCategoryButton(Identifier tag)
{
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), genderContainer.RectTransform),
TextManager.Get(gender.ToString()), style: "ListBoxElement")
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), menuCategoryContainer.RectTransform),
TextManager.Get(tag), style: "ListBoxElement")
{
UserData = gender,
UserData = tag,
OnClicked = OpenHeadSelection,
Selected = info.Gender == gender
Selected = info.Head.Preset.TagSet.Contains(tag)
};
}
createGenderButton(Gender.Male);
createGenderButton(Gender.Female);
int countAttachmentsOfType(WearableType wearableType)
=> info.FilterByTypeAndHeadID(
info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race),
wearableType, info.HeadSpriteId).Count();
foreach (var tag in info.Prefab.VarTags[info.Prefab.MenuCategoryVar].OrderBy(t => t.Value).Reverse())
{
createMenuCategoryButton(tag);
}
List<GUIScrollBar> attachmentSliders = new List<GUIScrollBar>();
void createAttachmentSlider(int initialValue, WearableType wearableType)
{
int attachmentCount = countAttachmentsOfType(wearableType);
int attachmentCount = info.CountValidAttachmentsOfType(wearableType);
if (attachmentCount > 0)
{
var labelTag = wearableType == WearableType.FaceAttachment
? "FaceAttachment.Accessories"
: $"FaceAttachment.{wearableType}";
? "FaceAttachment.Accessories".ToIdentifier()
: $"FaceAttachment.{wearableType}".ToIdentifier();
var sliderItemRT = createItemRectTransform(labelTag);
var slider =
new GUIScrollBar(sliderItemRT, style: "GUISlider")
@@ -670,12 +693,12 @@ namespace Barotrauma
}
}
createAttachmentSlider(info.HairIndex, WearableType.Hair);
createAttachmentSlider(info.BeardIndex, WearableType.Beard);
createAttachmentSlider(info.MoustacheIndex, WearableType.Moustache);
createAttachmentSlider(info.FaceAttachmentIndex, WearableType.FaceAttachment);
createAttachmentSlider(info.Head.HairIndex, WearableType.Hair);
createAttachmentSlider(info.Head.BeardIndex, WearableType.Beard);
createAttachmentSlider(info.Head.MoustacheIndex, WearableType.Moustache);
createAttachmentSlider(info.Head.FaceAttachmentIndex, WearableType.FaceAttachment);
void createColorSelector(string labelTag, IEnumerable<(Color Color, float Commonness)> options, Func<Color> getter,
void createColorSelector(Identifier labelTag, IEnumerable<(Color Color, float Commonness)> options, Func<Color> getter,
Action<Color> setter)
{
var selectorItemRT = createItemRectTransform(labelTag, 0.4f);
@@ -757,21 +780,21 @@ namespace Barotrauma
};
}
if (countAttachmentsOfType(WearableType.Hair) > 0)
if (info.CountValidAttachmentsOfType(WearableType.Hair) > 0)
{
createColorSelector($"Customization.{nameof(info.HairColor)}", info.HairColors,
() => info.HairColor, (color) => info.HairColor = color);
createColorSelector($"Customization.{nameof(info.Head.HairColor)}".ToIdentifier(), info.HairColors,
() => info.Head.HairColor, (color) => info.Head.HairColor = color);
}
if (countAttachmentsOfType(WearableType.Moustache) > 0 ||
countAttachmentsOfType(WearableType.Beard) > 0)
if (info.CountValidAttachmentsOfType(WearableType.Moustache) > 0 ||
info.CountValidAttachmentsOfType(WearableType.Beard) > 0)
{
createColorSelector($"Customization.{nameof(info.FacialHairColor)}", info.FacialHairColors,
() => info.FacialHairColor, (color) => info.FacialHairColor = color);
createColorSelector($"Customization.{nameof(info.Head.FacialHairColor)}".ToIdentifier(), info.FacialHairColors,
() => info.Head.FacialHairColor, (color) => info.Head.FacialHairColor = color);
}
createColorSelector($"Customization.{nameof(info.SkinColor)}", info.SkinColors, () => info.SkinColor,
(color) => info.SkinColor = color);
createColorSelector($"Customization.{nameof(info.Head.SkinColor)}".ToIdentifier(), info.SkinColors, () => info.Head.SkinColor,
(color) => info.Head.SkinColor = color);
RandomizeButton = new GUIButton(new RectTransform(Vector2.One * 0.12f,
parentComponent.RectTransform,
@@ -780,10 +803,11 @@ namespace Barotrauma
{
OnClicked = (button, o) =>
{
info.Head = new HeadInfo();
info.SetGenderAndRace(Rand.RandSync.Unsynced);
info.SetColors();
var headPreset = info.Prefab.Heads.GetRandom(Rand.RandSync.Unsynced);
info.Head = new HeadInfo(info, headPreset);
info.SetAttachments(Rand.RandSync.Unsynced);
info.SetColors(Rand.RandSync.Unsynced);
RecreateFrameContents();
info.RefreshHead();
OnHeadSwitch?.Invoke(this);
@@ -801,7 +825,7 @@ namespace Barotrauma
private bool OpenHeadSelection(GUIButton button, object userData)
{
Gender selectedGender = (Gender)userData;
Identifier selectedCategory = (Identifier)userData;
var info = CharacterInfo;
@@ -842,36 +866,27 @@ namespace Barotrauma
GUILayoutGroup row = null;
int itemsInRow = 0;
XElement headElement = info.Ragdoll.MainElement.Elements().FirstOrDefault(e =>
ContentXElement headElement = info.Ragdoll.MainElement.Elements().FirstOrDefault(e =>
e.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase));
XElement headSpriteElement = headElement.Element("sprite");
string spritePathWithTags = headSpriteElement.Attribute("texture").Value;
ContentXElement headSpriteElement = headElement.GetChildElement("sprite");
ContentPath spritePathWithTags = headSpriteElement.GetAttributeContentPath("texture");
var characterConfigElement = info.CharacterConfigElement;
var heads = info.Heads;
var heads = info.Prefab.Heads;
if (heads != null)
{
row = null;
itemsInRow = 0;
foreach (var kvp in heads.Where(kv => kv.Key.Gender == selectedGender))
foreach (var head in heads.Where(h => h.TagSet.Contains(selectedCategory)))
{
var headPreset = kvp.Key;
Race race = headPreset.Race;
int headIndex = headPreset.ID;
string spritePath = info.Prefab.ReplaceVars(spritePathWithTags.Value, head);
string spritePath = spritePathWithTags
.Replace("[GENDER]", selectedGender.ToString().ToLowerInvariant())
.Replace("[RACE]", race.ToString().ToLowerInvariant());
if (!File.Exists(spritePath))
{
continue;
}
if (!File.Exists(spritePath)) { continue; }
Sprite headSprite = new Sprite(headSpriteElement, "", spritePath);
headSprite.SourceRect =
new Rectangle(CalculateOffset(headSprite, kvp.Value.ToPoint()),
new Rectangle(CharacterInfo.CalculateOffset(headSprite, head.SheetIndex.ToPoint()),
headSprite.SourceRect.Size);
characterSprites.Add(headSprite);
@@ -881,7 +896,7 @@ namespace Barotrauma
new RectTransform(new Vector2(1.0f, 0.333f), HeadSelectionList.Content.RectTransform),
true)
{
UserData = selectedGender,
UserData = head.MenuCategory,
Visible = true
};
itemsInRow = 0;
@@ -892,9 +907,9 @@ namespace Barotrauma
{
OutlineColor = Color.White * 0.5f,
PressedColor = Color.White * 0.5f,
UserData = new Tuple<Gender, Race, int>(selectedGender, race, headIndex),
UserData = head,
OnClicked = SwitchHead,
Selected = selectedGender == info.Gender && race == info.Race && headIndex == info.HeadSpriteId,
Selected = info.Head.Preset == head,
Visible = true
};
@@ -909,12 +924,18 @@ namespace Barotrauma
private bool SwitchHead(GUIButton button, object obj)
{
var info = CharacterInfo;
Gender gender = ((Tuple<Gender, Race, int>)obj).Item1;
Race race = ((Tuple<Gender, Race, int>)obj).Item2;
int id = ((Tuple<Gender, Race, int>)obj).Item3;
info.Gender = gender;
info.Race = race;
info.Head.HeadSpriteId = id;
var headPreset = obj as HeadPreset;
if (info.Head.Preset != headPreset)
{
info.Head = new HeadInfo(info, headPreset)
{
SkinColor = info.Head.SkinColor,
HairColor = info.Head.HairColor,
FacialHairColor = info.Head.FacialHairColor
};
info.ReloadHeadAttachments();
}
RecreateFrameContents();
OnHeadSwitch?.Invoke(this);
return true;
@@ -927,16 +948,16 @@ namespace Barotrauma
switch (type)
{
case WearableType.Beard:
info.BeardIndex = index;
info.Head.BeardIndex = index;
break;
case WearableType.FaceAttachment:
info.FaceAttachmentIndex = index;
info.Head.FaceAttachmentIndex = index;
break;
case WearableType.Hair:
info.HairIndex = index;
info.Head.HairIndex = index;
break;
case WearableType.Moustache:
info.MoustacheIndex = index;
info.Head.MoustacheIndex = index;
break;
default:
DebugConsole.ThrowError($"Wearable type not implemented: {type}");

View File

@@ -112,395 +112,404 @@ namespace Barotrauma
}
}
public virtual void ClientWrite(IWriteMessage msg, object[] extraData = null)
public void ClientWriteInput(IWriteMessage msg)
{
if (extraData != null)
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
if (memInput.Count > 60)
{
switch ((NetEntityEvent.Type)extraData[0])
memInput.RemoveRange(60, memInput.Count - 60);
}
msg.Write(LastNetworkUpdateID);
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
msg.Write(inputCount);
for (int i = 0; i < inputCount; i++)
{
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
msg.Write(memInput[i].intAim);
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
memInput[i].states.HasFlag(InputNetFlags.Health) ||
memInput[i].states.HasFlag(InputNetFlags.Grab))
{
case NetEntityEvent.Type.InventoryState:
msg.WriteRangedInteger(0, 0, 4);
Inventory.ClientWrite(msg, extraData);
break;
case NetEntityEvent.Type.Treatment:
msg.WriteRangedInteger(1, 0, 4);
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
break;
case NetEntityEvent.Type.Status:
msg.WriteRangedInteger(2, 0, 4);
break;
case NetEntityEvent.Type.UpdateTalents:
msg.WriteRangedInteger(3, 0, 4);
msg.Write((ushort)characterTalents.Count);
foreach (var unlockedTalent in characterTalents)
{
msg.Write(unlockedTalent.Prefab.UIntIdentifier);
}
break;
msg.Write(memInput[i].interact);
}
}
}
public virtual void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
if (!(extraData is IEventData eventData)) { throw new Exception($"Malformed character event: expected {nameof(Character)}.{nameof(IEventData)}"); }
msg.WriteRangedInteger((int)eventData.EventType, (int)EventType.MinValue, (int)EventType.MaxValue);
switch (eventData)
{
case InventoryStateEventData inventoryStateEventData:
Inventory.ClientEventWrite(msg, inventoryStateEventData);
break;
case TreatmentEventData _:
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
break;
case CharacterStatusEventData _:
//do nothing
break;
case UpdateTalentsEventData _:
msg.Write((ushort)characterTalents.Count);
foreach (var unlockedTalent in characterTalents)
{
msg.Write(unlockedTalent.Prefab.UintIdentifier);
}
break;
default:
throw new Exception($"Malformed character event: did not expect {eventData.GetType().Name}");
}
}
public void ClientReadPosition(IReadMessage msg, float sendingTime)
{
bool facingRight = AnimController.Dir > 0.0f;
lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;
AnimController.Frozen = false;
Enabled = true;
UInt16 networkUpdateID = 0;
if (msg.ReadBoolean())
{
networkUpdateID = msg.ReadUInt16();
}
else
{
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
bool aimInput = msg.ReadBoolean();
keys[(int)InputType.Aim].Held = aimInput;
keys[(int)InputType.Aim].SetState(false, aimInput);
if (memInput.Count > 60)
bool shootInput = msg.ReadBoolean();
keys[(int)InputType.Shoot].Held = shootInput;
keys[(int)InputType.Shoot].SetState(false, shootInput);
bool useInput = msg.ReadBoolean();
keys[(int)InputType.Use].Held = useInput;
keys[(int)InputType.Use].SetState(false, useInput);
if (AnimController is HumanoidAnimController)
{
memInput.RemoveRange(60, memInput.Count - 60);
bool crouching = msg.ReadBoolean();
keys[(int)InputType.Crouch].Held = crouching;
keys[(int)InputType.Crouch].SetState(false, crouching);
}
msg.Write(LastNetworkUpdateID);
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
msg.Write(inputCount);
for (int i = 0; i < inputCount; i++)
{
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
msg.Write(memInput[i].intAim);
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
memInput[i].states.HasFlag(InputNetFlags.Health) ||
memInput[i].states.HasFlag(InputNetFlags.Grab))
{
msg.Write(memInput[i].interact);
}
}
bool attackInput = msg.ReadBoolean();
keys[(int)InputType.Attack].Held = attackInput;
keys[(int)InputType.Attack].SetState(false, attackInput);
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
TransformCursorPos();
bool ragdollInput = msg.ReadBoolean();
keys[(int)InputType.Ragdoll].Held = ragdollInput;
keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);
facingRight = msg.ReadBoolean();
}
msg.WritePadBits();
}
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
switch (type)
bool entitySelected = msg.ReadBoolean();
Character selectedCharacter = null;
Item selectedItem = null;
AnimController.Animation animation = AnimController.Animation.None;
if (entitySelected)
{
case ServerNetObject.ENTITY_POSITION:
bool facingRight = AnimController.Dir > 0.0f;
lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;
AnimController.Frozen = false;
Enabled = true;
UInt16 networkUpdateID = 0;
if (msg.ReadBoolean())
ushort characterID = msg.ReadUInt16();
ushort itemID = msg.ReadUInt16();
selectedCharacter = FindEntityByID(characterID) as Character;
selectedItem = FindEntityByID(itemID) as Item;
if (characterID != NullEntityID)
{
bool doingCpr = msg.ReadBoolean();
if (doingCpr && SelectedCharacter != null)
{
networkUpdateID = msg.ReadUInt16();
animation = AnimController.Animation.CPR;
}
else
{
bool aimInput = msg.ReadBoolean();
keys[(int)InputType.Aim].Held = aimInput;
keys[(int)InputType.Aim].SetState(false, aimInput);
bool shootInput = msg.ReadBoolean();
keys[(int)InputType.Shoot].Held = shootInput;
keys[(int)InputType.Shoot].SetState(false, shootInput);
bool useInput = msg.ReadBoolean();
keys[(int)InputType.Use].Held = useInput;
keys[(int)InputType.Use].SetState(false, useInput);
if (AnimController is HumanoidAnimController)
{
bool crouching = msg.ReadBoolean();
keys[(int)InputType.Crouch].Held = crouching;
keys[(int)InputType.Crouch].SetState(false, crouching);
}
bool attackInput = msg.ReadBoolean();
keys[(int)InputType.Attack].Held = attackInput;
keys[(int)InputType.Attack].SetState(false, attackInput);
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
TransformCursorPos();
bool ragdollInput = msg.ReadBoolean();
keys[(int)InputType.Ragdoll].Held = ragdollInput;
keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);
facingRight = msg.ReadBoolean();
}
bool entitySelected = msg.ReadBoolean();
Character selectedCharacter = null;
Item selectedItem = null;
AnimController.Animation animation = AnimController.Animation.None;
if (entitySelected)
{
ushort characterID = msg.ReadUInt16();
ushort itemID = msg.ReadUInt16();
selectedCharacter = FindEntityByID(characterID) as Character;
selectedItem = FindEntityByID(itemID) as Item;
if (characterID != NullEntityID)
{
bool doingCpr = msg.ReadBoolean();
if (doingCpr && SelectedCharacter != null)
{
animation = AnimController.Animation.CPR;
}
}
}
Vector2 pos = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
Vector2 linearVelocity = new Vector2(
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
bool fixedRotation = msg.ReadBoolean();
float? rotation = null;
float? angularVelocity = null;
if (!fixedRotation)
{
rotation = msg.ReadSingle();
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
}
bool readStatus = msg.ReadBoolean();
if (readStatus)
{
ReadStatus(msg);
AIController?.ClientRead(msg);
}
msg.ReadPadBits();
int index = 0;
if (GameMain.Client.Character == this && CanMove)
{
var posInfo = new CharacterStateInfo(
pos, rotation,
networkUpdateID,
facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation);
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
index++;
memState.Insert(index, posInfo);
}
else
{
var posInfo = new CharacterStateInfo(
pos, rotation,
linearVelocity, angularVelocity,
sendingTime, facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation);
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
index++;
memState.Insert(index, posInfo);
}
break;
case ServerNetObject.ENTITY_EVENT:
int eventType = msg.ReadRangedInteger(0, 13);
switch (eventType)
{
case 0: //NetEntityEvent.Type.InventoryState
if (Inventory == null)
{
string errorMsg = "Received an inventory update message for an entity with no inventory ([name], removed: " + Removed + ")";
DebugConsole.ThrowError(errorMsg.Replace("[name]", Name));
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", SpeciesName));
//read anyway to prevent messing up reading the rest of the message
_ = msg.ReadUInt16();
byte inventoryItemCount = msg.ReadByte();
for (int i = 0; i < inventoryItemCount; i++)
{
msg.ReadUInt16();
}
}
else
{
Inventory.ClientRead(type, msg, sendingTime);
}
break;
case 1: //NetEntityEvent.Type.Control
bool myCharacter = msg.ReadBoolean();
byte ownerID = msg.ReadByte();
ResetNetState();
if (myCharacter)
{
if (controlled != null)
{
LastNetworkUpdateID = controlled.LastNetworkUpdateID;
}
if (!IsDead) { Controlled = this; }
IsRemotePlayer = false;
GameMain.Client.HasSpawned = true;
GameMain.Client.Character = this;
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f;
GameMain.Client.WaitForNextRoundRespawn = null;
}
else
{
if (controlled == this)
{
Controlled = null;
IsRemotePlayer = ownerID > 0;
}
}
break;
case 2: //NetEntityEvent.Type.Status
ReadStatus(msg);
break;
case 3: //NetEntityEvent.Type.UpdateSkills
int skillCount = msg.ReadByte();
for (int i = 0; i < skillCount; i++)
{
string skillIdentifier = msg.ReadString();
float skillLevel = msg.ReadSingle();
info?.SetSkillLevel(skillIdentifier, skillLevel);
}
break;
case 4: // NetEntityEvent.Type.SetAttackTarget
case 5: //NetEntityEvent.Type.ExecuteAttack
int attackLimbIndex = msg.ReadByte();
UInt16 targetEntityID = msg.ReadUInt16();
int targetLimbIndex = msg.ReadByte();
Vector2 targetSimPos = new Vector2(msg.ReadSingle(), msg.ReadSingle());
//255 = entity already removed, no need to do anything
if (attackLimbIndex == 255 || Removed) { break; }
if (attackLimbIndex >= AnimController.Limbs.Length)
{
DebugConsole.ThrowError($"Received invalid {(eventType == 4 ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
break;
}
Limb attackLimb = AnimController.Limbs[attackLimbIndex];
Limb targetLimb = null;
IDamageable targetEntity = FindEntityByID(targetEntityID) as IDamageable;
if (targetEntity == null && eventType == 4)
{
DebugConsole.ThrowError($"Received invalid SetAttackTarget message. Target entity not found (ID {targetEntityID})");
break;
}
if (targetEntity is Character targetCharacter)
{
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
{
DebugConsole.ThrowError($"Received invalid {(eventType == 4 ? "SetAttackTarget" : "ExecuteAttack")} message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
break;
}
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
}
if (attackLimb?.attack != null && Controlled != this)
{
if (eventType == 4)
{
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
}
else
{
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
}
}
break;
case 6: //NetEntityEvent.Type.AssignCampaignInteraction
byte campaignInteractionType = msg.ReadByte();
bool requireConsciousness = msg.ReadBoolean();
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
RequireConsciousnessForCustomInteract = requireConsciousness;
break;
case 7: //NetEntityEvent.Type.ObjectiveManagerState
// 1 = order, 2 = objective
int msgType = msg.ReadRangedInteger(0, 2);
if (msgType == 0) { break; }
bool validData = msg.ReadBoolean();
if (!validData) { break; }
if (msgType == 1)
{
int orderIndex = msg.ReadRangedInteger(0, Order.PrefabList.Count);
var orderPrefab = Order.PrefabList[orderIndex];
string option = null;
if (orderPrefab.HasOptions)
{
int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
if (optionIndex > -1)
{
option = orderPrefab.AllOptions[optionIndex];
}
}
GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
}
else if (msgType == 2)
{
string identifier = msg.ReadString();
string option = msg.ReadString();
ushort objectiveTargetEntityId = msg.ReadUInt16();
var objectiveTargetEntity = FindEntityByID(objectiveTargetEntityId);
GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity);
}
break;
case 8: //NetEntityEvent.Type.TeamChange
byte newTeamId = msg.ReadByte();
ChangeTeam((CharacterTeamType)newTeamId);
break;
case 9: //NetEntityEvent.Type.AddToCrew
GameMain.GameSession.CrewManager.AddCharacter(this);
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{
ushort itemID = msg.ReadUInt16();
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
item.AllowStealing = true;
var wifiComponent = item.GetComponent<WifiComponent>();
if (wifiComponent != null)
{
wifiComponent.TeamID = teamID;
}
var idCard = item.GetComponent<IdCard>();
if (idCard != null)
{
idCard.TeamID = teamID;
idCard.SubmarineSpecificID = 0;
}
}
break;
case 10: //NetEntityEvent.Type.UpdateExperience
int experienceAmount = msg.ReadInt32();
info?.SetExperience(experienceAmount);
break;
case 11: //NetEntityEvent.Type.UpdateTalents:
ushort talentCount = msg.ReadUInt16();
for (int i = 0; i < talentCount; i++)
{
bool addedThisRound = msg.ReadBoolean();
UInt32 talentIdentifier = msg.ReadUInt32();
GiveTalent(talentIdentifier, addedThisRound);
}
break;
case 12: //NetEntityEvent.Type.UpdateMoney:
int moneyAmount = msg.ReadInt32();
SetMoney(moneyAmount);
break;
case 13: //NetEntityEvent.Type.UpdatePermanentStats:
byte savedStatValueCount = msg.ReadByte();
StatTypes statType = (StatTypes)msg.ReadByte();
info?.ClearSavedStatValues(statType);
for (int i = 0; i < savedStatValueCount; i++)
{
string statIdentifier = msg.ReadString();
float statValue = msg.ReadSingle();
bool removeOnDeath = msg.ReadBoolean();
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
}
break;
}
msg.ReadPadBits();
break;
}
}
Vector2 pos = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
Vector2 linearVelocity = new Vector2(
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
bool fixedRotation = msg.ReadBoolean();
float? rotation = null;
float? angularVelocity = null;
if (!fixedRotation)
{
rotation = msg.ReadSingle();
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
}
bool readStatus = msg.ReadBoolean();
if (readStatus)
{
ReadStatus(msg);
AIController?.ClientRead(msg);
}
msg.ReadPadBits();
int index = 0;
if (GameMain.Client.Character == this && CanMove)
{
var posInfo = new CharacterStateInfo(
pos, rotation,
networkUpdateID,
facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation);
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
index++;
memState.Insert(index, posInfo);
}
else
{
var posInfo = new CharacterStateInfo(
pos, rotation,
linearVelocity, angularVelocity,
sendingTime, facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation);
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
index++;
memState.Insert(index, posInfo);
}
}
public virtual void ClientEventRead(IReadMessage msg, float sendingTime)
{
EventType eventType = (EventType)msg.ReadRangedInteger((int)EventType.MinValue, (int)EventType.MaxValue);
switch (eventType)
{
case EventType.InventoryState:
if (Inventory == null)
{
string errorMsg = "Received an inventory update message for an entity with no inventory ([name], removed: " + Removed + ")";
DebugConsole.ThrowError(errorMsg.Replace("[name]", Name));
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", SpeciesName.Value));
//read anyway to prevent messing up reading the rest of the message
_ = msg.ReadUInt16();
byte inventoryItemCount = msg.ReadByte();
for (int i = 0; i < inventoryItemCount; i++)
{
msg.ReadUInt16();
}
}
else
{
Inventory.ClientEventRead(msg, sendingTime);
}
break;
case EventType.Control:
bool myCharacter = msg.ReadBoolean();
byte ownerID = msg.ReadByte();
ResetNetState();
if (myCharacter)
{
if (controlled != null)
{
LastNetworkUpdateID = controlled.LastNetworkUpdateID;
}
if (!IsDead) { Controlled = this; }
IsRemotePlayer = false;
GameMain.Client.HasSpawned = true;
GameMain.Client.Character = this;
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f;
GameMain.Client.WaitForNextRoundRespawn = null;
}
else
{
if (controlled == this)
{
Controlled = null;
}
if (GameMain.Client?.Character == this)
{
GameMain.Client.Character = null;
}
IsRemotePlayer = ownerID > 0;
}
break;
case EventType.Status:
ReadStatus(msg);
break;
case EventType.UpdateSkills:
int skillCount = msg.ReadByte();
for (int i = 0; i < skillCount; i++)
{
Identifier skillIdentifier = msg.ReadIdentifier();
float skillLevel = msg.ReadSingle();
info?.SetSkillLevel(skillIdentifier, skillLevel);
}
break;
case EventType.SetAttackTarget:
case EventType.ExecuteAttack:
int attackLimbIndex = msg.ReadByte();
UInt16 targetEntityID = msg.ReadUInt16();
int targetLimbIndex = msg.ReadByte();
float targetX = msg.ReadSingle();
float targetY = msg.ReadSingle();
Vector2 targetSimPos = new Vector2(targetX, targetY);
//255 = entity already removed, no need to do anything
if (attackLimbIndex == 255 || Removed) { break; }
if (attackLimbIndex >= AnimController.Limbs.Length)
{
string errorMsg = $"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Character.ClientEventRead:AttackLimbOutOfBounds", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
break;
}
Limb attackLimb = AnimController.Limbs[attackLimbIndex];
Limb targetLimb = null;
IDamageable targetEntity = FindEntityByID(targetEntityID) as IDamageable;
if (targetEntity == null && eventType == EventType.SetAttackTarget)
{
DebugConsole.ThrowError($"Received invalid SetAttackTarget message. Target entity not found (ID {targetEntityID})");
GameAnalyticsManager.AddErrorEventOnce("Character.ClientEventRead:TargetNotFound", GameAnalyticsManager.ErrorSeverity.Error, "Received invalid SetAttackTarget message. Target entity not found.");
break;
}
if (targetEntity is Character targetCharacter && targetLimbIndex != 255)
{
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
{
DebugConsole.ThrowError($"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
string errorMsgWithoutName = $"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Target limb index out of bounds (target character: {targetCharacter.SpeciesName}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})";
GameAnalyticsManager.AddErrorEventOnce("Character.ClientEventRead:TargetLimbOutOfBounds", GameAnalyticsManager.ErrorSeverity.Error, errorMsgWithoutName);
break;
}
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
}
if (attackLimb?.attack != null && Controlled != this)
{
if (eventType == EventType.SetAttackTarget)
{
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
}
else
{
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
}
}
break;
case EventType.AssignCampaignInteraction:
byte campaignInteractionType = msg.ReadByte();
bool requireConsciousness = msg.ReadBoolean();
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
RequireConsciousnessForCustomInteract = requireConsciousness;
break;
case EventType.ObjectiveManagerState:
// 1 = order, 2 = objective
AIObjectiveManager.ObjectiveType msgType
= (AIObjectiveManager.ObjectiveType)msg.ReadRangedInteger(
(int)AIObjectiveManager.ObjectiveType.MinValue,
(int)AIObjectiveManager.ObjectiveType.MaxValue);
if (msgType == 0) { break; }
bool validData = msg.ReadBoolean();
if (!validData) { break; }
if (msgType == AIObjectiveManager.ObjectiveType.Order)
{
UInt32 orderPrefabUintIdentifier = msg.ReadUInt32();
var orderPrefab = OrderPrefab.Prefabs.Find(p => p.UintIdentifier == orderPrefabUintIdentifier);
Identifier option = Identifier.Empty;
if (orderPrefab.HasOptions)
{
int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
if (optionIndex > -1)
{
option = orderPrefab.AllOptions[optionIndex];
}
}
GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
}
else if (msgType == AIObjectiveManager.ObjectiveType.Objective)
{
Identifier identifier = msg.ReadIdentifier();
Identifier option = msg.ReadIdentifier();
ushort objectiveTargetEntityId = msg.ReadUInt16();
var objectiveTargetEntity = FindEntityByID(objectiveTargetEntityId);
GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity);
}
break;
case EventType.TeamChange:
byte newTeamId = msg.ReadByte();
ChangeTeam((CharacterTeamType)newTeamId);
break;
case EventType.AddToCrew:
GameMain.GameSession.CrewManager.AddCharacter(this);
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{
ushort itemID = msg.ReadUInt16();
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
item.AllowStealing = true;
var wifiComponent = item.GetComponent<WifiComponent>();
if (wifiComponent != null)
{
wifiComponent.TeamID = teamID;
}
var idCard = item.GetComponent<IdCard>();
if (idCard != null)
{
idCard.TeamID = teamID;
idCard.SubmarineSpecificID = 0;
}
}
break;
case EventType.UpdateExperience:
int experienceAmount = msg.ReadInt32();
info?.SetExperience(experienceAmount);
break;
case EventType.UpdateTalents:
ushort talentCount = msg.ReadUInt16();
for (int i = 0; i < talentCount; i++)
{
bool addedThisRound = msg.ReadBoolean();
UInt32 talentIdentifier = msg.ReadUInt32();
GiveTalent(talentIdentifier, addedThisRound);
}
break;
case EventType.UpdateMoney:
int moneyAmount = msg.ReadInt32();
SetMoney(moneyAmount);
break;
case EventType.UpdatePermanentStats:
byte savedStatValueCount = msg.ReadByte();
StatTypes statType = (StatTypes)msg.ReadByte();
info?.ClearSavedStatValues(statType);
for (int i = 0; i < savedStatValueCount; i++)
{
string statIdentifier = msg.ReadString();
float statValue = msg.ReadSingle();
bool removeOnDeath = msg.ReadBoolean();
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
}
break;
}
msg.ReadPadBits();
}
public static Character ReadSpawnData(IReadMessage inc)
@@ -542,9 +551,11 @@ namespace Barotrauma
{
bool hasOwner = inc.ReadBoolean();
int ownerId = hasOwner ? inc.ReadByte() : -1;
int balance = inc.ReadInt32();
int rewardDistribution = inc.ReadRangedInteger(0, 100);
byte teamID = inc.ReadByte();
bool hasAi = inc.ReadBoolean();
string infoSpeciesName = inc.ReadString();
Identifier infoSpeciesName = inc.ReadIdentifier();
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
try
@@ -558,6 +569,12 @@ namespace Barotrauma
}
character.TeamID = (CharacterTeamType)teamID;
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
if (character.CampaignInteractionType == CampaignMode.InteractionType.Store)
{
character.MerchantIdentifier = inc.ReadIdentifier();
}
character.Wallet.Balance = balance;
character.Wallet.RewardDistribution = rewardDistribution;
if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
{
(GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(character, character.CampaignInteractionType);
@@ -567,7 +584,7 @@ namespace Barotrauma
int orderCount = inc.ReadByte();
for (int i = 0; i < orderCount; i++)
{
int orderPrefabIndex = inc.ReadByte();
UInt32 orderPrefabUintIdentifier = inc.ReadUInt32();
Entity targetEntity = FindEntityByID(inc.ReadUInt16());
Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null;
int orderOptionIndex = inc.ReadByte();
@@ -581,18 +598,23 @@ namespace Barotrauma
targetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true);
}
if (orderPrefabIndex >= 0 && orderPrefabIndex < Order.PrefabList.Count)
OrderPrefab orderPrefab =
OrderPrefab.Prefabs.Find(p => p.UintIdentifier == orderPrefabUintIdentifier);
if (orderPrefab != null)
{
var orderPrefab = Order.PrefabList[orderPrefabIndex];
var component = orderPrefab.GetTargetItemComponent(targetEntity as Item);
if (!orderPrefab.MustSetTarget || (targetEntity != null && component != null) || targetPosition != null)
{
var order = targetPosition == null ?
new Order(orderPrefab, targetEntity, component, orderGiver: orderGiver) :
new Order(orderPrefab, targetPosition, orderGiver: orderGiver);
character.SetOrder(order,
orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null,
orderPriority, orderGiver, speak: false, force: true);
order = order.WithOption(
orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length
? orderPrefab.Options[orderOptionIndex]
: Identifier.Empty)
.WithManualPriority(orderPriority)
.WithOrderGiver(orderGiver);
character.SetOrder(order, isNewOrder: true, speak: false, force: true);
}
else
{
@@ -601,7 +623,7 @@ namespace Barotrauma
}
else
{
DebugConsole.ThrowError("Invalid order prefab index - index (" + orderPrefabIndex + ") out of bounds.");
DebugConsole.ThrowError("Invalid order prefab index - index (" + orderPrefabUintIdentifier + ") out of bounds.");
}
}

View File

@@ -1,4 +1,5 @@
using Barotrauma.Sounds;
using System.Collections.Immutable;
namespace Barotrauma
{
@@ -13,20 +14,17 @@ namespace Barotrauma
public readonly CharacterParams.SoundParams Params;
public SoundType Type => Params.State;
public Gender Gender => Params.Gender;
public ImmutableHashSet<Identifier> TagSet => Params.TagSet;
public float Volume => roundSound == null ? 0.0f : roundSound.Volume;
public float Range => roundSound == null ? 0.0f : roundSound.Range;
public Sound Sound => roundSound?.Sound;
public bool IgnoreMuffling
{
get { return roundSound?.IgnoreMuffling ?? false; }
}
public bool IgnoreMuffling => roundSound?.IgnoreMuffling ?? false;
public CharacterSound(CharacterParams.SoundParams soundParams)
{
Params = soundParams;
roundSound = Submarine.LoadRoundSound(soundParams.Element);
roundSound = RoundSound.Load(soundParams.Element);
}
}
}

View File

@@ -39,7 +39,7 @@ namespace Barotrauma
public Vector2 Size;
private readonly Submarine parentSub;
public string Text
public LocalizedString Text
{
get;
private set;
@@ -58,7 +58,7 @@ namespace Barotrauma
}
public HUDProgressBar(Vector2 worldPosition, string textTag, Submarine parentSubmarine = null)
: this(worldPosition, parentSubmarine, GUI.Style.Red, GUI.Style.Green, textTag)
: this(worldPosition, parentSubmarine, GUIStyle.Red, GUIStyle.Green, textTag)
{
}
@@ -73,7 +73,7 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(textTag))
{
this.textTag = textTag;
Text = TextManager.Get(textTag);
Text = TextManager.Get(textTag).Fallback(textTag);
}
}
@@ -101,12 +101,12 @@ namespace Barotrauma
color * a,
Color.White * a * 0.8f);
if (!string.IsNullOrEmpty(Text))
if (!Text.IsNullOrEmpty())
{
Vector2 textSize = GUI.SmallFont.MeasureString(Text);
Vector2 textSize = GUIStyle.SmallFont.MeasureString(Text);
Vector2 textPos = new Vector2(pos.X + (Size.X - textSize.X) / 2, pos.Y - textSize.Y * 1.2f);
GUI.DrawString(spriteBatch, textPos - Vector2.One, Text, Color.Black * a, font: GUI.SmallFont);
GUI.DrawString(spriteBatch, textPos, Text, Color.White * a, font: GUI.SmallFont);
GUI.DrawString(spriteBatch, textPos - Vector2.One, Text, Color.Black * a, font: GUIStyle.SmallFont);
GUI.DrawString(spriteBatch, textPos, Text, Color.White * a, font: GUIStyle.SmallFont);
}
}

View File

@@ -1,37 +0,0 @@
namespace Barotrauma
{
partial class AfflictionHusk : Affliction
{
private InfectionState? prevDisplayedMessage;
partial void UpdateMessages()
{
if (character != Character.Controlled) { return; }
if (Prefab is AfflictionPrefabHusk { SendMessages: false }) { return; }
if (prevDisplayedMessage.HasValue && prevDisplayedMessage.Value == State) { return; }
switch (State)
{
case InfectionState.Dormant:
if (Strength < DormantThreshold * 0.5f)
{
return;
}
GUI.AddMessage(TextManager.Get("HuskDormant"), GUI.Style.Red);
break;
case InfectionState.Transition:
GUI.AddMessage(TextManager.Get("HuskCantSpeak"), GUI.Style.Red);
break;
case InfectionState.Active:
if (character.Params.UseHuskAppendage)
{
GUI.AddMessage(TextManager.GetWithVariable("HuskActivate", "[Attack]", GameMain.Config.KeyBindText(InputType.Attack)), GUI.Style.Red);
}
break;
case InfectionState.Final:
default:
break;
}
prevDisplayedMessage = State;
}
}
}

View File

@@ -75,7 +75,7 @@ namespace Barotrauma
case FloodType.Minor:
currentFloodState += deltaTime;
//lerp the water surface in all hulls 15 units above the floor within 10 seconds
foreach (Hull hull in Hull.hullList)
foreach (Hull hull in Hull.HullList)
{
for (int i = hull.FakeFireSources.Count - 1; i >= 0; i--)
{
@@ -87,7 +87,7 @@ namespace Barotrauma
case FloodType.Major:
currentFloodState += deltaTime;
//create a full flood in 10 seconds
foreach (Hull hull in Hull.hullList)
foreach (Hull hull in Hull.HullList)
{
for (int i = hull.FakeFireSources.Count - 1; i >= 0; i--)
{
@@ -98,7 +98,7 @@ namespace Barotrauma
break;
case FloodType.HideFlooding:
//hide water inside hulls (the player can't see which hulls are flooded)
foreach (Hull hull in Hull.hullList)
foreach (Hull hull in Hull.HullList)
{
hull.DrawSurface = hull.Rect.Y - hull.Rect.Height;
}
@@ -140,7 +140,7 @@ namespace Barotrauma
character.Submarine != null &&
createFireSourceTimer > MathHelper.Lerp(MaxFakeFireSourceInterval, MinFakeFireSourceInterval, Strength / 100.0f))
{
Hull fireHull = Hull.hullList.GetRandom(h => h.Submarine == character.Submarine);
Hull fireHull = Hull.HullList.GetRandomUnsynced(h => h.Submarine == character.Submarine);
if (fireHull != null)
{
var fakeFire = new DummyFireSource(Vector2.One * 500.0f, new Vector2(Rand.Range(fireHull.WorldRect.X, fireHull.WorldRect.Right), fireHull.WorldPosition.Y + 1), fireHull, isNetworkMessage: true)

View File

@@ -14,11 +14,31 @@ namespace Barotrauma
{
private static bool toggledThisFrame;
public static Sprite DamageOverlay;
public class DamageOverlayPrefab : Prefab
{
public readonly static PrefabSelector<DamageOverlayPrefab> Prefabs = new PrefabSelector<DamageOverlayPrefab>();
public static string DamageOverlayFile;
public readonly Sprite DamageOverlay;
private static string[] strengthTexts;
public DamageOverlayPrefab(ContentXElement element, AfflictionsFile file) : base(file, file.Path.Value.ToIdentifier())
{
DamageOverlay = new Sprite(element);
}
public override void Dispose()
{
DamageOverlay.Remove();
}
}
public static Sprite DamageOverlay => DamageOverlayPrefab.Prefabs.ActivePrefab.DamageOverlay;
private readonly static LocalizedString[] strengthTexts = new LocalizedString[]
{
TextManager.Get("AfflictionStrengthLow"),
TextManager.Get("AfflictionStrengthMedium"),
TextManager.Get("AfflictionStrengthHigh")
};
private Point screenResolution;
@@ -134,7 +154,7 @@ namespace Barotrauma
Character.Controlled.ResetInteract = true;
if (openHealthWindow != null)
{
if (value.Character.Info == null || value.Character == Character.Controlled || Character.Controlled.HasEquippedItem("healthscanner"))
if (value.Character.Info == null || value.Character == Character.Controlled || Character.Controlled.HasEquippedItem("healthscanner".ToIdentifier()))
{
openHealthWindow.characterName.Text = value.Character.Name;
}
@@ -173,20 +193,10 @@ namespace Barotrauma
private GUIFrame healthBarHolder;
partial void InitProjSpecific(XElement element, Character character)
partial void InitProjSpecific(ContentXElement element, Character character)
{
DisplayedVitality = MaxVitality;
if (strengthTexts == null)
{
strengthTexts = new string[]
{
TextManager.Get("AfflictionStrengthLow"),
TextManager.Get("AfflictionStrengthMedium"),
TextManager.Get("AfflictionStrengthHigh")
};
}
character.OnAttacked += OnAttacked;
healthWindow = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.6f), GUI.Canvas, anchor: Anchor.Center, scaleBasis: ScaleBasis.Smallest), style: "GUIFrameListBox");
@@ -200,13 +210,13 @@ namespace Barotrauma
{
Stretch = true
};
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform, Anchor.CenterLeft),
onDraw: (spriteBatch, component) =>
{
character.Info?.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), Vector2.Zero, component.Rect.Width, false, character != Character.Controlled);
});
characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont)
characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont)
{
AutoScaleHorizontal = true
};
@@ -220,12 +230,12 @@ namespace Barotrauma
var healthBarContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.07f), healthWindowVerticalLayout.RectTransform), style: null);
var healthBarIcon = new GUIFrame(new RectTransform(new Vector2(0.095f, 1.0f), healthBarContainer.RectTransform), style: "GUIHealthBarIcon");
healthWindowHealthBarShadow = new GUIProgressBar(new RectTransform(new Vector2(0.91f, 1.0f), healthBarContainer.RectTransform, Anchor.CenterRight),
barSize: 1.0f, color: GUI.Style.Green, style: "GUIHealthBar")
barSize: 1.0f, color: GUIStyle.Green, style: "GUIHealthBar")
{
IsHorizontal = true
};
healthWindowHealthBar = new GUIProgressBar(new RectTransform(new Vector2(0.91f, 1.0f), healthBarContainer.RectTransform, Anchor.CenterRight),
barSize: 1.0f, color: GUI.Style.Green, style: "GUIHealthBar")
barSize: 1.0f, color: GUIStyle.Green, style: "GUIHealthBar")
{
IsHorizontal = true
};
@@ -290,7 +300,7 @@ namespace Barotrauma
if (GameMain.Client != null)
{
GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Treatment });
GameMain.Client.CreateEntityEvent(Character.Controlled, new Character.TreatmentEventData());
}
return true;
@@ -311,7 +321,7 @@ namespace Barotrauma
}
);
deadIndicator = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.1f), limbSelection.RectTransform, Anchor.Center),
text: TextManager.Get("Deceased"), font: GUI.LargeFont, textAlignment: Alignment.Center, style: "GUIToolTip")
text: TextManager.Get("Deceased"), font: GUIStyle.LargeFont, textAlignment: Alignment.Center, style: "GUIToolTip")
{
Visible = false,
CanBeFocused = false
@@ -328,7 +338,7 @@ namespace Barotrauma
afflictionIconContainer = new GUIListBox(new RectTransform(new Vector2(0.25f, 1.0f), characterIndicatorArea.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), healthWindowVerticalLayout.RectTransform),
TextManager.Get("SuitableTreatments"), font: GUI.SubHeadingFont, textAlignment: Alignment.BottomCenter);
TextManager.Get("SuitableTreatments"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomCenter);
treatmentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), healthWindowVerticalLayout.RectTransform), true)
{
@@ -366,10 +376,10 @@ namespace Barotrauma
healthShadowSize = 1.0f;
healthBar = new GUIProgressBar(new RectTransform(Vector2.One, healthBarHolder.RectTransform, Anchor.BottomRight),
barSize: 1.0f, color: GUI.Style.HealthBarColorHigh, style: "CharacterHealthBar")
barSize: 1.0f, color: GUIStyle.HealthBarColorHigh, style: "CharacterHealthBar")
{
HoverCursor = CursorState.Hand,
ToolTip = TextManager.GetWithVariable("hudbutton.healthinterface", "[key]", GameMain.Config.KeyBindText(InputType.Health)),
ToolTip = TextManager.GetWithVariable("hudbutton.healthinterface", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)),
Enabled = true
};
@@ -390,7 +400,7 @@ namespace Barotrauma
{
if (GameMain.Client != null)
{
GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Status });
GameMain.Client.CreateEntityEvent(Character.Controlled, new Character.CharacterStatusEventData());
}
else
{
@@ -406,7 +416,7 @@ namespace Barotrauma
if (element != null)
{
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -501,7 +511,17 @@ namespace Barotrauma
{
if (prevOxygen > 0.0f && OxygenAmount <= 0.0f && Character.Controlled == Character)
{
SoundPlayer.PlaySound(Character.Info != null && Character.Info.Gender == Gender.Female ? "drownfemale" : "drownmale");
string soundName;
if (Character.Info != null)
{
soundName = Character.Info.ReplaceVars($"drown[{Character.Info.Prefab.MenuCategoryVar}]");
}
else
{
var charInfoPrefab = CharacterPrefab.HumanPrefab.CharacterInfoPrefab;
soundName = charInfoPrefab.ReplaceVars($"drown[{charInfoPrefab.MenuCategoryVar}]", charInfoPrefab.Heads.First());
}
SoundPlayer.PlaySound(soundName);
}
if (Character == Character.Controlled && !IsUnconscious && !Character.IsDead && OxygenAmount < LowOxygenThreshold)
@@ -553,10 +573,10 @@ namespace Barotrauma
inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir,
limb.WorldPosition, velocity, 0.0f, Character.AnimController.CurrentHull);
if (blood != null && !inWater)
if (blood != null)
{
blood.Size *= bloodParticleSize;
if (!string.IsNullOrEmpty(Character.BloodDecalName) && Rand.Range(0.0f, 1.0f) < 0.05f)
if (!inWater && !string.IsNullOrEmpty(Character.BloodDecalName) && Rand.Range(0.0f, 1.0f) < 0.05f)
{
blood.OnCollision += (Vector2 pos, Hull hull) =>
{
@@ -715,11 +735,11 @@ namespace Barotrauma
if (!(afflictionIcon.UserData is Affliction affliction)) { continue; }
if (affliction.AppliedAsFailedTreatmentTime > Timing.TotalTime - 1.0 && afflictionIcon.FlashTimer <= 0.0f)
{
afflictionIcon.Flash(GUI.Style.Red);
afflictionIcon.Flash(GUIStyle.Red);
}
else if (affliction.AppliedAsSuccessfulTreatmentTime > Timing.TotalTime - 1.0 && afflictionIcon.FlashTimer <= 0.0f)
{
afflictionIcon.Flash(GUI.Style.Green);
afflictionIcon.Flash(GUIStyle.Green);
}
}
@@ -754,7 +774,7 @@ namespace Barotrauma
var labelContainer = afflictionTooltip.Content.GetChildByUserData("label");
labelContainer.RectTransform.Resize(new Point(labelContainer.Rect.Width, (int)(GUI.LargeFont.Size * 1.5f)));
labelContainer.RectTransform.Resize(new Point(labelContainer.Rect.Width, (int)(GUIStyle.LargeFont.Size * 1.5f)));
}
}
else
@@ -799,7 +819,7 @@ namespace Barotrauma
{
var treatmentButton = component.GetChild<GUIButton>();
if (!(treatmentButton?.UserData is ItemPrefab itemPrefab)) { continue; }
var matchingItem = Character.Controlled.Inventory.FindItem(it => it.prefab == itemPrefab, recursive: true);
var matchingItem = Character.Controlled.Inventory.FindItem(it => it.Prefab == itemPrefab, recursive: true);
treatmentButton.Enabled = matchingItem != null;
if (treatmentButton.Enabled && treatmentButton.State == GUIComponent.ComponentState.Hover)
{
@@ -809,16 +829,18 @@ namespace Barotrauma
if (Character.Controlled.Inventory.visualSlots != null && index > -1 && index < Character.Controlled.Inventory.visualSlots.Length &&
Character.Controlled.Inventory.visualSlots[index].HighlightTimer <= 0.0f)
{
Character.Controlled.Inventory.visualSlots[index].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f);
Character.Controlled.Inventory.visualSlots[index].ShowBorderHighlight(GUIStyle.Green, 0.5f, 0.5f);
}
}
if (matchingItem != null && !string.IsNullOrEmpty(treatmentButton.ToolTip)) { continue; }
treatmentButton.ToolTip = $"‖color:255,255,255,255‖{itemPrefab.Name}‖color:end‖" + '\n' + itemPrefab.Description;
if (matchingItem != null && !treatmentButton.ToolTip.IsNullOrEmpty()) { continue; }
treatmentButton.ToolTip = RichString.Rich($"‖color:255,255,255,255‖{itemPrefab.Name}‖color:end‖" + '\n' + itemPrefab.Description);
if (treatmentButton.Enabled)
{
treatmentButton.ToolTip =
$"‖color:gui.green‖[{TextManager.Get(PlayerInput.MouseButtonsSwapped() ? "input.rightmouse" : "input.leftmouse")}] {TextManager.Get("quickuseaction.usetreatment")}‖color:end‖" + '\n'
+ treatmentButton.RawToolTip;
RichString.Rich(
$"‖color:gui.green‖[{TextManager.Get(PlayerInput.MouseButtonsSwapped() ? "input.rightmouse" : "input.leftmouse")}] "
+ $"{TextManager.Get("quickuseaction.usetreatment")}‖color:end‖" + '\n'
+ treatmentButton.ToolTip.NestedStr);
}
foreach (GUIComponent child in treatmentButton.Children)
{
@@ -834,7 +856,7 @@ namespace Barotrauma
}
else
{
healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(DisplayedVitality / MaxVitality, GUI.Style.HealthBarColorLow, GUI.Style.HealthBarColorMedium, GUI.Style.HealthBarColorHigh);
healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(DisplayedVitality / MaxVitality, GUIStyle.HealthBarColorLow, GUIStyle.HealthBarColorMedium, GUIStyle.HealthBarColorHigh);
healthBar.HoverColor = healthWindowHealthBar.HoverColor = healthBar.Color * 2.0f;
healthBar.BarSize = healthWindowHealthBar.BarSize =
(DisplayedVitality > 0.0f) ?
@@ -1007,14 +1029,14 @@ namespace Barotrauma
DrawStatusHUD(spriteBatch);
}
private (Affliction affliction, string text)? highlightedAfflictionIcon;
private (Affliction Affliction, LocalizedString NameToolTip)? highlightedAfflictionIcon = null;
public void DrawStatusHUD(SpriteBatch spriteBatch)
{
highlightedAfflictionIcon = null;
//Rectangle interactArea = healthBar.Rect;
if (Character.Controlled?.SelectedCharacter == null && openHealthWindow == null)
{
List<(Affliction affliction, string text)> statusIcons = new List<(Affliction affliction, string text)>();
var statusIcons = new List<(Affliction Affliction, LocalizedString Warning)>();
if (Character.InPressure)
{
statusIcons.Add((pressureAffliction, TextManager.Get("PressureHUDWarning")));
@@ -1045,7 +1067,7 @@ namespace Barotrauma
foreach (var statusIcon in statusIcons)
{
Affliction affliction = statusIcon.affliction;
Affliction affliction = statusIcon.Affliction;
AfflictionPrefab afflictionPrefab = affliction.Prefab;
Rectangle afflictionIconRect = new Rectangle(pos, new Point(iconSize));
@@ -1059,10 +1081,10 @@ namespace Barotrauma
{
Rectangle glowRect = afflictionIconRect;
glowRect.Inflate((int)(20 * GUI.Scale), (int)(20 * GUI.Scale));
var glow = GUI.Style.GetComponentStyle("OuterGlowCircular");
var glow = GUIStyle.GetComponentStyle("OuterGlowCircular");
glow.Sprites[GUIComponent.ComponentState.None][0].Draw(
spriteBatch, glowRect,
GUI.Style.Red * (float)((Math.Sin(affliction.DamagePerSecondTimer * MathHelper.TwoPi - MathHelper.PiOver2) + 1.0f) * 0.5f));
GUIStyle.Red * (float)((Math.Sin(affliction.DamagePerSecondTimer * MathHelper.TwoPi - MathHelper.PiOver2) + 1.0f) * 0.5f));
}
float alphaMultiplier = highlightedAfflictionIcon == statusIcon ? 1f : 0.8f;
@@ -1082,8 +1104,8 @@ namespace Barotrauma
if (highlightedAfflictionIcon != null)
{
string nameTooltip = highlightedAfflictionIcon.Value.text;
Vector2 offset = GUI.Font.MeasureString(nameTooltip);
LocalizedString nameTooltip = highlightedAfflictionIcon.Value.NameToolTip;
Vector2 offset = GUIStyle.Font.MeasureString(nameTooltip);
GUI.DrawString(spriteBatch,
alignment == Alignment.Left ? highlightedIconPos + offset : highlightedIconPos - offset,
@@ -1096,7 +1118,7 @@ namespace Barotrauma
float currHealth = healthBar.BarSize;
Color prevColor = healthBar.Color;
healthBarShadow.BarSize = healthShadowSize;
healthBarShadow.Color = Color.Lerp(GUI.Style.Red, Color.Black, 0.5f);
healthBarShadow.Color = Color.Lerp(GUIStyle.Red, Color.Black, 0.5f);
healthBarShadow.Visible = true;
healthBar.BarSize = currHealth;
healthBar.Color = prevColor;
@@ -1113,7 +1135,7 @@ namespace Barotrauma
float currHealth = healthWindowHealthBar.BarSize;
Color prevColor = healthWindowHealthBar.Color;
healthWindowHealthBarShadow.BarSize = healthShadowSize;
healthWindowHealthBarShadow.Color = GUI.Style.Red;
healthWindowHealthBarShadow.Color = GUIStyle.Red;
healthWindowHealthBarShadow.Visible = true;
healthWindowHealthBar.BarSize = currHealth;
healthWindowHealthBar.Color = prevColor;
@@ -1137,10 +1159,10 @@ namespace Barotrauma
{
if (prefab.IsBuff)
{
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUI.Style.BuffColorLow, GUI.Style.BuffColorMedium, GUI.Style.BuffColorHigh);
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUIStyle.BuffColorLow, GUIStyle.BuffColorMedium, GUIStyle.BuffColorHigh);
}
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUI.Style.DebuffColorLow, GUI.Style.DebuffColorMedium, GUI.Style.DebuffColorHigh);
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUIStyle.DebuffColorLow, GUIStyle.DebuffColorMedium, GUIStyle.DebuffColorHigh);
}
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, prefab.IconColors);
@@ -1213,7 +1235,7 @@ namespace Barotrauma
CanBeFocused = false
};
var progressbarBg = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.18f), content.RectTransform), 0.0f, GUI.Style.Green, style: "GUIAfflictionBar")
var progressbarBg = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.18f), content.RectTransform), 0.0f, GUIStyle.Green, style: "GUIAfflictionBar")
{
UserData = "afflictionstrengthprediction",
CanBeFocused = false
@@ -1240,9 +1262,9 @@ namespace Barotrauma
afflictionIcon.PressedColor = afflictionIcon.Color;
afflictionIcon.HoverColor = Color.Lerp(afflictionIcon.Color, Color.White, 0.6f);
afflictionIcon.SelectedColor = Color.Lerp(afflictionIcon.Color, Color.White, 0.5f);
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.1f, 0.0f), content.RectTransform),
affliction.Prefab.Name, font: GUI.SmallFont, textAlignment: Alignment.BottomCenter)
affliction.Prefab.Name, font: GUIStyle.SmallFont, textAlignment: Alignment.BottomCenter)
{
CanBeFocused = false
};
@@ -1274,13 +1296,13 @@ namespace Barotrauma
//key = item identifier
//float = suitability
Dictionary<string, float> treatmentSuitability = new Dictionary<string, float>();
Dictionary<Identifier, float> treatmentSuitability = new Dictionary<Identifier, float>();
GetSuitableTreatments(treatmentSuitability,
normalize: true,
ignoreHiddenAfflictions: true,
limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex));
foreach (string treatment in treatmentSuitability.Keys.ToList())
foreach (Identifier treatment in treatmentSuitability.Keys.ToList())
{
//prefer suggestions for items the player has
if (Character.Controlled.Inventory.FindItemByIdentifier(treatment, recursive: true) != null)
@@ -1304,10 +1326,10 @@ namespace Barotrauma
recommendedTreatmentContainer.AutoHideScrollBar = true;
}
List<KeyValuePair<string, float>> treatmentSuitabilities = treatmentSuitability.OrderByDescending(t => t.Value).ToList();
List<KeyValuePair<Identifier, float>> treatmentSuitabilities = treatmentSuitability.OrderByDescending(t => t.Value).ToList();
int count = 0;
foreach (KeyValuePair<string, float> treatment in treatmentSuitabilities)
foreach (KeyValuePair<Identifier, float> treatment in treatmentSuitabilities)
{
count++;
if (count > 5) { break; }
@@ -1326,7 +1348,7 @@ namespace Barotrauma
OnClicked = (btn, userdata) =>
{
if (!(userdata is ItemPrefab itemPrefab)) { return false; }
var item = Character.Controlled.Inventory.FindItem(it => it.prefab == itemPrefab, recursive: true);
var item = Character.Controlled.Inventory.FindItem(it => it.Prefab == itemPrefab, recursive: true);
if (item == null) { return false; }
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex);
item.ApplyTreatment(Character.Controlled, Character, targetLimb);
@@ -1337,15 +1359,15 @@ namespace Barotrauma
new GUIImage(new RectTransform(Vector2.One, innerFrame.RectTransform, Anchor.Center), style: "TalentBackgroundGlow")
{
CanBeFocused = false,
Color = GUI.Style.Green,
Color = GUIStyle.Green,
HoverColor = Color.White,
PressedColor = Color.DarkGray,
SelectedColor = Color.Transparent,
DisabledColor = Color.Transparent
};
Sprite itemSprite = item.InventoryIcon ?? item.sprite;
Color itemColor = itemSprite == item.sprite ? item.SpriteColor : item.InventoryIconColor;
Sprite itemSprite = item.InventoryIcon ?? item.Sprite;
Color itemColor = itemSprite == item.Sprite ? item.SpriteColor : item.InventoryIconColor;
var itemIcon = new GUIImage(new RectTransform(new Vector2(0.8f, 0.8f), innerFrame.RectTransform, Anchor.Center),
itemSprite, scaleToFit: true)
{
@@ -1397,12 +1419,12 @@ namespace Barotrauma
CanBeFocused = false
};
var afflictionName = new GUITextBlock(new RectTransform(new Vector2(0.65f, 1.0f), labelContainer.RectTransform), affliction.Prefab.Name, textAlignment: Alignment.CenterLeft, font: GUI.LargeFont)
var afflictionName = new GUITextBlock(new RectTransform(new Vector2(0.65f, 1.0f), labelContainer.RectTransform), affliction.Prefab.Name, textAlignment: Alignment.CenterLeft, font: GUIStyle.LargeFont)
{
CanBeFocused = false,
AutoScaleHorizontal = true
};
var afflictionStrength = new GUITextBlock(new RectTransform(new Vector2(0.35f, 0.6f), labelContainer.RectTransform), "", textAlignment: Alignment.TopRight, font: GUI.SubHeadingFont)
var afflictionStrength = new GUITextBlock(new RectTransform(new Vector2(0.35f, 0.6f), labelContainer.RectTransform), "", textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont)
{
UserData = "strength",
CanBeFocused = false
@@ -1423,21 +1445,21 @@ namespace Barotrauma
if (description.Font.MeasureString(description.WrappedText).Y > description.Rect.Height)
{
description.Font = GUI.SmallFont;
description.Font = GUIStyle.SmallFont;
}
Point nameDims = new Point(afflictionName.Rect.Width, (int)(GUI.LargeFont.Size * 1.5f));
Point nameDims = new Point(afflictionName.Rect.Width, (int)(GUIStyle.LargeFont.Size * 1.5f));
afflictionStrength.Text = strengthTexts[
MathHelper.Clamp((int)Math.Floor((affliction.Strength / affliction.Prefab.MaxStrength) * strengthTexts.Length), 0, strengthTexts.Length - 1)];
Vector2 strengthDims = GUI.SubHeadingFont.MeasureString(afflictionStrength.Text);
Vector2 strengthDims = GUIStyle.SubHeadingFont.MeasureString(afflictionStrength.Text);
labelContainer.RectTransform.Resize(new Point(labelContainer.Rect.Width, nameDims.Y));
afflictionName.RectTransform.Resize(new Point((int)(labelContainer.Rect.Width - strengthDims.X * 0.99f), nameDims.Y));
afflictionStrength.RectTransform.Resize(new Point(labelContainer.Rect.Width - afflictionName.Rect.Width, nameDims.Y));
afflictionStrength.TextColor = Color.Lerp(GUI.Style.Orange, GUI.Style.Red,
afflictionStrength.TextColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red,
affliction.Strength / affliction.Prefab.MaxStrength);
description.RectTransform.Resize(new Point(description.Rect.Width, (int)(description.TextSize.Y + 10)));
@@ -1451,8 +1473,8 @@ namespace Barotrauma
{
vitality.Visible = true;
vitality.Text = TextManager.Get("Vitality") + " -" + vitalityDecrease;
vitality.TextColor = vitalityDecrease <= 0 ? GUI.Style.Green :
Color.Lerp(GUI.Style.Orange, GUI.Style.Red, affliction.Strength / affliction.Prefab.MaxStrength);
vitality.TextColor = vitalityDecrease <= 0 ? GUIStyle.Green :
Color.Lerp(GUIStyle.Orange, GUIStyle.Red, affliction.Strength / affliction.Prefab.MaxStrength);
}
vitality.AutoDraw = true;
@@ -1478,7 +1500,7 @@ namespace Barotrauma
var potentialTreatment = Inventory.DraggingItems.FirstOrDefault();
if (potentialTreatment == null && GUI.MouseOn?.UserData is ItemPrefab itemPrefab)
{
potentialTreatment = Character.Controlled.Inventory.FindItem(it => it.prefab == itemPrefab, recursive: true);
potentialTreatment = Character.Controlled.Inventory.FindItem(it => it.Prefab == itemPrefab, recursive: true);
}
potentialTreatment ??= Inventory.SelectedSlot?.Item;
@@ -1488,11 +1510,11 @@ namespace Barotrauma
Color afflictionEffectColor = Color.White;
if (afflictionVitalityDecrease > 0.0f)
{
afflictionEffectColor = GUI.Style.Red;
afflictionEffectColor = GUIStyle.Red;
}
else if (afflictionVitalityDecrease < 0.0f)
{
afflictionEffectColor = GUI.Style.Green;
afflictionEffectColor = GUIStyle.Green;
}
var child = afflictionIconContainer.Content.FindChild(affliction);
@@ -1510,7 +1532,7 @@ namespace Barotrauma
if (afflictionStrengthPrediction < affliction.Strength)
{
afflictionStrengthBar.Color = afflictionEffectColor;
afflictionStrengthPredictionBar.Color = GUI.Style.Blue * t;
afflictionStrengthPredictionBar.Color = GUIStyle.Blue * t;
afflictionStrengthPredictionBar.BarSize = afflictionStrengthBar.BarSize;
afflictionStrengthBar.BarSize = afflictionStrengthPrediction / affliction.Prefab.MaxStrength;
}
@@ -1541,8 +1563,8 @@ namespace Barotrauma
{
foreach (var reduceAffliction in effect.ReduceAffliction)
{
if (reduceAffliction.affliction != affliction.Identifier && reduceAffliction.affliction != affliction.Prefab.AfflictionType) { continue; }
strength -= reduceAffliction.amount * (effect.Duration > 0 ? effect.Duration : 1.0f);
if (reduceAffliction.AfflictionIdentifier != affliction.Identifier && reduceAffliction.AfflictionIdentifier != affliction.Prefab.AfflictionType) { continue; }
strength -= reduceAffliction.ReduceAmount * (effect.Duration > 0 ? effect.Duration : 1.0f);
}
foreach (var addAffliction in effect.Afflictions)
{
@@ -1563,7 +1585,7 @@ namespace Barotrauma
strengthText.Text = strengthTexts[
MathHelper.Clamp((int)Math.Floor((affliction.Strength / affliction.Prefab.MaxStrength) * strengthTexts.Length), 0, strengthTexts.Length - 1)];
strengthText.TextColor = Color.Lerp(GUI.Style.Orange, GUI.Style.Red,
strengthText.TextColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red,
affliction.Strength / affliction.Prefab.MaxStrength);
var vitalityText = labelContainer.GetChildByUserData("vitality") as GUITextBlock;
@@ -1576,8 +1598,8 @@ namespace Barotrauma
{
vitalityText.Visible = true;
vitalityText.Text = TextManager.Get("Vitality") + " -" + vitalityDecrease;
vitalityText.TextColor = vitalityDecrease <= 0 ? GUI.Style.Green :
Color.Lerp(GUI.Style.Orange, GUI.Style.Red, affliction.Strength / affliction.Prefab.MaxStrength);
vitalityText.TextColor = vitalityDecrease <= 0 ? GUIStyle.Green :
Color.Lerp(GUIStyle.Orange, GUIStyle.Red, affliction.Strength / affliction.Prefab.MaxStrength);
}
}
@@ -1807,9 +1829,9 @@ namespace Barotrauma
if (afflictionsDisplayedOnLimb.Count() > 1)
{
string additionalAfflictionCount = $"+{afflictionsDisplayedOnLimb.Count() - 1}";
Vector2 displace = GUI.SubHeadingFont.MeasureString(additionalAfflictionCount);
GUI.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X * 1.1f, -displace.Y * 0.45f), Color.Black * 0.75f);
GUI.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X, -displace.Y * 0.5f), Color.White);
Vector2 displace = GUIStyle.SubHeadingFont.MeasureString(additionalAfflictionCount);
GUIStyle.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X * 1.1f, -displace.Y * 0.45f), Color.Black * 0.75f);
GUIStyle.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X, -displace.Y * 0.5f), Color.White);
}
i++;
@@ -1885,7 +1907,7 @@ namespace Barotrauma
for (int i = 0; i < afflictionCount; i++)
{
uint afflictionID = inc.ReadUInt32();
AfflictionPrefab afflictionPrefab = AfflictionPrefab.Prefabs.Find(p => p.UIntIdentifier == afflictionID);
AfflictionPrefab afflictionPrefab = AfflictionPrefab.Prefabs.Find(p => p.UintIdentifier == afflictionID);
if (afflictionPrefab == null)
{
DebugConsole.ThrowError("Error while reading character health data: affliction with the uint ID " + afflictionID + " not found.");
@@ -1913,7 +1935,7 @@ namespace Barotrauma
{
int limbIndex = inc.ReadRangedInteger(0, limbHealths.Count - 1);
uint afflictionID = inc.ReadUInt32();
AfflictionPrefab afflictionPrefab = AfflictionPrefab.Prefabs.Find(p => p.UIntIdentifier == afflictionID);
AfflictionPrefab afflictionPrefab = AfflictionPrefab.Prefabs.Find(p => p.UintIdentifier == afflictionID);
if (afflictionPrefab == null)
{
DebugConsole.ThrowError("Error while reading character health data: affliction with the uint ID " + afflictionID + " not found.");

View File

@@ -2,14 +2,14 @@
{
partial class DamageModifier
{
[Serialize("", false), Editable]
[Serialize("", IsPropertySaveable.No), Editable]
public string DamageSound
{
get;
private set;
}
[Serialize("", false), Editable]
[Serialize("", IsPropertySaveable.No), Editable]
public string DamageParticle
{
get;

View File

@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace Barotrauma
{
partial class JobPrefab : IPrefab, IDisposable
partial class JobPrefab : PrefabWithUintIdentifier
{
public GUIButton CreateInfoFrame(out GUIComponent buttonContainer)
{
@@ -18,20 +18,20 @@ namespace Barotrauma
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, height), frameHolder.RectTransform, Anchor.Center));
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), Name, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), Name, font: GUIStyle.LargeFont);
var descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.15f) },
Description, font: GUI.SmallFont, wrap: true);
Description, font: GUIStyle.SmallFont, wrap: true);
var skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform)
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) });
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillContainer.RectTransform),
TextManager.Get("Skills"), font: GUI.LargeFont);
TextManager.Get("Skills"), font: GUIStyle.LargeFont);
foreach (SkillPrefab skill in Skills)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillContainer.RectTransform),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + skill.Identifier), (int)skill.LevelRange.Start + " - " + (int)skill.LevelRange.End),
font: GUI.SmallFont);
font: GUIStyle.SmallFont);
}
buttonContainer = paddedFrame;
@@ -43,14 +43,14 @@ namespace Barotrauma
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
TextManager.Get("Items", fallBackTag: "mapentitycategory.equipment"), font: GUI.LargeFont);
TextManager.Get("Items", "mapentitycategory.equipment"), font: GUIStyle.LargeFont);
foreach (string identifier in itemIdentifiers.Distinct())
{
if (!(MapEntityPrefab.Find(name: null, identifier: identifier) is ItemPrefab itemPrefab)) { continue; }
int count = itemIdentifiers.Count(i => i == identifier);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
" - " + (count == 1 ? itemPrefab.Name : itemPrefab.Name + " x" + count),
font: GUI.SmallFont);
font: GUIStyle.SmallFont);
}*/
return frameHolder;
@@ -76,12 +76,12 @@ namespace Barotrauma
}
}
public List<OutfitPreview> GetJobOutfitSprites(Gender gender, bool useInventoryIcon, out Vector2 maxDimensions)
public List<OutfitPreview> GetJobOutfitSprites(CharacterInfoPrefab charInfoPrefab, bool useInventoryIcon, out Vector2 maxDimensions)
{
List<OutfitPreview> outfitPreviews = new List<OutfitPreview>();
maxDimensions = Vector2.One;
var equipIdentifiers = Element.GetChildElements("ItemSet").Elements().Where(e => e.GetAttributeBool("outfit", false)).Select(e => e.GetAttributeString("identifier", ""));
var equipIdentifiers = Element.GetChildElements("ItemSet").Elements().Where(e => e.GetAttributeBool("outfit", false)).Select(e => e.GetAttributeIdentifier("identifier", ""));
List<ItemPrefab> outfitPrefabs = new List<ItemPrefab>();
foreach (var equipIdentifier in equipIdentifiers)
@@ -114,8 +114,8 @@ namespace Barotrauma
var children = previewElement.Elements().ToList();
for (int n = 0; n < children.Count; n++)
{
XElement spriteElement = children[n];
string spriteTexture = spriteElement.GetAttributeString("texture", "").Replace("[GENDER]", (gender == Gender.Female) ? "female" : "male");
var spriteElement = children[n];
string spriteTexture = charInfoPrefab.ReplaceVars(spriteElement.GetAttributeString("texture", ""), charInfoPrefab.Heads.First());
var sprite = new Sprite(spriteElement, file: spriteTexture);
sprite.size = new Vector2(sprite.SourceRect.Width, sprite.SourceRect.Height);
outfitPreview.AddSprite(sprite, children[n].GetAttributeVector2("offset", Vector2.Zero));

View File

@@ -78,7 +78,7 @@ namespace Barotrauma
//{
// var pos = ConvertUnits.ToDisplayUnits(mouthPos.Value);
// pos.Y = -pos.Y;
// ShapeExtensions.DrawPoint(spriteBatch, pos, GUI.Style.Red, size: 5);
// ShapeExtensions.DrawPoint(spriteBatch, pos, GUIStyle.Red, size: 5);
//}
// A debug visualisation on the bezier curve between limbs.
@@ -95,7 +95,7 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch, start, end, Color.White);
GUI.DrawLine(spriteBatch, start, control, Color.Black);
GUI.DrawLine(spriteBatch, control, end, Color.Black);
GUI.DrawBezierWithDots(spriteBatch, start, end, control, 1000, GUI.Style.Red);*/
GUI.DrawBezierWithDots(spriteBatch, start, end, control, 1000, GUIStyle.Red);*/
}
}
@@ -274,7 +274,7 @@ namespace Barotrauma
}
}
partial void InitProjSpecific(XElement element)
partial void InitProjSpecific(ContentXElement element)
{
for (int i = 0; i < Params.decorativeSpriteParams.Count; i++)
{
@@ -290,7 +290,7 @@ namespace Barotrauma
spriteAnimState.Add(decorativeSprite, new SpriteState());
}
TintMask = null;
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -317,7 +317,7 @@ namespace Barotrauma
NonConditionalDeformations.AddRange(deformations);
break;
case "randomcolor":
randomColor = subElement.GetAttributeColorArray("colors", null)?.GetRandom();
randomColor = subElement.GetAttributeColorArray("colors", null)?.GetRandomUnsynced();
if (randomColor.HasValue)
{
Params.GetSprite().Color = randomColor.Value;
@@ -337,8 +337,8 @@ namespace Barotrauma
InitialLightSpriteAlpha = LightSource.OverrideLightSpriteAlpha;
break;
case "tintmask":
string tintMaskPath = subElement.GetAttributeString("texture", "");
if (!string.IsNullOrWhiteSpace(tintMaskPath))
ContentPath tintMaskPath = subElement.GetAttributeContentPath("texture");
if (!tintMaskPath.IsNullOrWhiteSpace())
{
TintMask = new Sprite(subElement, file: GetSpritePath(tintMaskPath));
TintHighlightThreshold = subElement.GetAttributeFloat("highlightthreshold", 0.6f);
@@ -346,8 +346,8 @@ namespace Barotrauma
}
break;
case "huskmask":
string huskMaskPath = subElement.GetAttributeString("texture", "");
if (!string.IsNullOrWhiteSpace(huskMaskPath))
ContentPath huskMaskPath = subElement.GetAttributeContentPath("texture");
if (!huskMaskPath.IsNullOrWhiteSpace())
{
HuskMask = new Sprite(subElement, file: GetSpritePath(huskMaskPath));
}
@@ -387,7 +387,7 @@ namespace Barotrauma
}
if (deformation == null)
{
deformation = SpriteDeformation.Load(animationElement, character.SpeciesName);
deformation = SpriteDeformation.Load(animationElement, character.SpeciesName.Value);
if (deformation != null)
{
ragdoll.SpriteDeformations.Add(deformation);
@@ -472,19 +472,23 @@ namespace Barotrauma
}
private string _texturePath;
private string GetSpritePath(XElement element, SpriteParams spriteParams)
private string GetSpritePath(ContentXElement element, SpriteParams spriteParams)
{
if (_texturePath == null)
{
if (spriteParams != null)
{
string texturePath = character.Params.VariantFile?.Root?.GetAttributeString("texture", null) ?? spriteParams.GetTexturePath();
ContentPath texturePath =
character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage)
?? ContentPath.FromRaw(character.Prefab.ContentPackage, spriteParams.GetTexturePath());
_texturePath = GetSpritePath(texturePath);
}
else
{
string texturePath = element.GetAttributeString("texture", null);
texturePath = string.IsNullOrWhiteSpace(texturePath) ? ragdoll.RagdollParams.Texture : texturePath;
ContentPath texturePath = element.GetAttributeContentPath("texture");
texturePath = texturePath.IsNullOrWhiteSpace()
? ContentPath.FromRaw(character.Prefab.ContentPackage, ragdoll.RagdollParams.Texture)
: texturePath;
_texturePath = GetSpritePath(texturePath);
}
}
@@ -494,20 +498,18 @@ namespace Barotrauma
/// <summary>
/// Get the full path of a limb sprite, taking into account tags, gender and head id
/// </summary>
public static string GetSpritePath(string texturePath, CharacterInfo characterInfo)
public static string GetSpritePath(ContentPath texturePath, CharacterInfo characterInfo)
{
string spritePath = texturePath;
string spritePath = texturePath.Value;
string spritePathWithTags = spritePath;
if (characterInfo != null)
{
spritePath = spritePath.Replace("[GENDER]", (characterInfo.Gender == Gender.Female) ? "female" : "male");
spritePath = spritePath.Replace("[RACE]", characterInfo.Race.ToString().ToLowerInvariant());
spritePath = spritePath.Replace("[HEADID]", characterInfo.HeadSpriteId.ToString());
spritePath = characterInfo.ReplaceVars(spritePath);
if (characterInfo.HeadSprite != null && characterInfo.SpriteTags.Any())
{
string tags = "";
characterInfo.SpriteTags.ForEach(tag => tags += "[" + tag + "]");
characterInfo.SpriteTags.ForEach(tag => tags += $"[{tag}]");
spritePathWithTags = Path.Combine(
Path.GetDirectoryName(spritePath),
@@ -518,10 +520,10 @@ namespace Barotrauma
}
private string GetSpritePath(string texturePath)
private string GetSpritePath(ContentPath texturePath)
{
if (!character.IsHumanoid) { return texturePath; }
return GetSpritePath(texturePath, character?.Info);
if (!character.IsHumanoid) { return texturePath.Value; }
return GetSpritePath(texturePath, character.Info);
}
partial void LoadParamsProjSpecific()
@@ -696,7 +698,7 @@ namespace Barotrauma
clr = clr.Multiply(ragdoll.RagdollParams.Color);
if (character.Info != null)
{
clr = clr.Multiply(character.Info.SkinColor);
clr = clr.Multiply(character.Info.Head.SkinColor);
}
if (character.CharacterHealth.FaceTint.A > 0 && type == LimbType.Head)
{
@@ -929,7 +931,7 @@ namespace Barotrauma
if (pullJoint != null)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(pullJoint.WorldAnchorB);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), GUI.Style.Red, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), GUIStyle.Red, true);
}
var bodyDrawPos = body.DrawPosition;
bodyDrawPos.Y = -bodyDrawPos.Y;
@@ -943,11 +945,11 @@ namespace Barotrauma
var front = ConvertUnits.ToDisplayUnits(body.FarseerBody.GetWorldPoint(localFront));
front.Y = -front.Y;
GUI.DrawLine(spriteBatch, bodyDrawPos, front, Color.Yellow, width: 2);
GUI.DrawLine(spriteBatch, from, to, GUI.Style.Red, width: 1);
GUI.DrawLine(spriteBatch, from, to, GUIStyle.Red, width: 1);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)from.X, (int)from.Y, 12, 12), Color.White, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)to.X, (int)to.Y, 12, 12), Color.White, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)from.X, (int)from.Y, 10, 10), Color.Blue, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)to.X, (int)to.Y, 10, 10), GUI.Style.Red, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)to.X, (int)to.Y, 10, 10), GUIStyle.Red, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)front.X, (int)front.Y, 10, 10), Color.Yellow, true);
//Vector2 mainLimbFront = ConvertUnits.ToDisplayUnits(ragdoll.MainLimb.body.FarseerBody.GetWorldPoint(ragdoll.MainLimb.body.GetFrontLocal(MathHelper.ToRadians(limbParams.Orientation))));
@@ -1046,8 +1048,8 @@ namespace Barotrauma
//{
// width = (int)Math.Round(width / cam.Zoom);
//}
//GUI.DrawLine(spriteBatch, startPos, startPos + Vector2.Normalize(up) * size, GUI.Style.Red, width: width);
Color color = modifier.DamageMultiplier > 1 ? GUI.Style.Red : GUI.Style.Green;
//GUI.DrawLine(spriteBatch, startPos, startPos + Vector2.Normalize(up) * size, GUIStyle.Red, width: width);
Color color = modifier.DamageMultiplier > 1 ? GUIStyle.Red : GUIStyle.Green;
float size = ConvertUnits.ToDisplayUnits(body.GetSize().Length() / 2);
if (isScreenSpace)
{
@@ -1078,9 +1080,9 @@ namespace Barotrauma
{
wearable.Sprite.SourceRect = new Rectangle(CharacterInfo.CalculateOffset(sprite, wearable.SheetIndex.Value), sprite.SourceRect.Size);
}
else if (type == LimbType.Head && character.Info != null && character.Info.Head.SheetIndex.HasValue)
else if (type == LimbType.Head && character.Info != null)
{
wearable.Sprite.SourceRect = new Rectangle(CharacterInfo.CalculateOffset(sprite, character.Info.Head.SheetIndex.Value.ToPoint()), sprite.SourceRect.Size);
wearable.Sprite.SourceRect = new Rectangle(CharacterInfo.CalculateOffset(sprite, character.Info.Head.SheetIndex.ToPoint()), sprite.SourceRect.Size);
}
else
{
@@ -1134,11 +1136,11 @@ namespace Barotrauma
{
if (wearable.Type == WearableType.Hair)
{
wearableColor = character.Info.HairColor;
wearableColor = character.Info.Head.HairColor;
}
else if (wearable.Type == WearableType.Beard || wearable.Type == WearableType.Moustache)
{
wearableColor = character.Info.FacialHairColor;
wearableColor = character.Info.Head.FacialHairColor;
}
}
float scale = wearable.Scale;
@@ -1164,22 +1166,22 @@ namespace Barotrauma
wearable.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), finalColor, origin, rotation, scale, spriteEffect, depth);
}
private WearableSprite GetWearableSprite(WearableType type, bool random = false)
private WearableSprite GetWearableSprite(WearableType type)//, bool random = false)
{
var info = character.Info;
if (info == null) { return null; }
XElement element;
if (random)
ContentXElement element;
/*if (random)
{
element = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Gender, info.Race), type, info.Head.HeadSpriteId)?.GetRandom(Rand.RandSync.ClientOnly);
element = info.FilterElements(info.Wearables, info.Head.Preset.TagSet)?.GetRandom(Rand.RandSync.ClientOnly);
}
else
{
element = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Gender, info.Race), type, info.Head.HeadSpriteId)?.FirstOrDefault();
}
{*/
element = info.FilterElements(info.Wearables, info.Head.Preset.TagSet, type)?.FirstOrDefault();
//}
if (element != null)
{
return new WearableSprite(element.Element("sprite"), type);
return new WearableSprite(element.GetChildElement("sprite"), type);
}
return null;
}

View File

@@ -0,0 +1,167 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.IO;
namespace Barotrauma
{
public class ModProject
{
public class File
{
private File(string path, Type type)
{
Path = path.CleanUpPathCrossPlatform(correctFilenameCase: false);
Type = type switch
{
_ when !type.IsSubclassOf(typeof(ContentFile)) => throw new ArgumentException($"{type.Name} does not derive from {nameof(ContentFile)}"),
{ IsAbstract: true } => throw new ArgumentException($"{type.Name} is abstract"),
_ => type
};
}
private File(ContentFile f)
{
Path = f.Path.RawValue ?? "";
Type = f.GetType();
}
public static File FromContentFile(ContentFile file)
=> new File(file);
public static File FromPath<T>(string path) where T : ContentFile
=> new File(path, typeof(T));
/// <summary>
/// Prefer FromPath&lt;T&gt; when possible, this just exists
/// for cases where the type can only be decided at runtime
/// </summary>
public static File FromPath(string path, Type type)
=> new File(path, type);
public readonly string Path;
public readonly Type Type;
public XElement ToXElement()
{
if (Type is null) { throw new InvalidOperationException("Type must be set before calling ToXElement"); }
if (Path.IsNullOrEmpty()) { throw new InvalidOperationException("Path must be set before calling ToXElement"); }
return new XElement(Type.Name.RemoveFromEnd("File"), new XAttribute("file", Path));
}
}
public ModProject() { }
public ModProject(ContentPackage? contentPackage)
{
if (contentPackage is null) { return; }
Name = contentPackage.Name;
AltNames = contentPackage.AltNames.ToList();
files = contentPackage.Files.Select(File.FromContentFile).ToList();
ModVersion = IncrementModVersion(contentPackage.ModVersion);
IsCore = contentPackage is CorePackage;
SteamWorkshopId = contentPackage.SteamWorkshopId;
ExpectedHash = contentPackage.Hash;
InstallTime = contentPackage.InstallTime;
}
private string name = "";
public string Name
{
get => name;
set
{
var charsToRemove = Path.GetInvalidFileNameChars();
name = string.Concat(value.Where(c => !charsToRemove.Contains(c)));
}
}
public readonly List<string> AltNames = new List<string>();
private readonly List<File> files = new List<File>();
public IReadOnlyList<File> Files => files;
public string ModVersion = ContentPackage.DefaultModVersion;
public Md5Hash? ExpectedHash { get; private set; }
public bool IsCore = false;
public UInt64 SteamWorkshopId = 0;
public DateTime? InstallTime = null;
public bool HasFile(File file)
=> Files.Any(f =>
string.Equals(f.Path, file.Path, StringComparison.OrdinalIgnoreCase)
&& f.Type == file.Type);
public void AddFile(File file)
{
if (!HasFile(file))
{
files.Add(file);
DiscardHashAndInstallTime();
}
}
public void DiscardHashAndInstallTime()
{
ExpectedHash = null;
InstallTime = null;
}
public static string IncrementModVersion(string modVersion)
{
//look for an integer at the end of the string and increment it
int startIndex = modVersion.Length - 1;
while (char.IsDigit(modVersion[startIndex])) { startIndex--; }
startIndex++;
if (startIndex >= modVersion.Length
|| !char.IsDigit(modVersion[startIndex])
|| !int.TryParse(
modVersion[startIndex..],
NumberStyles.Any,
CultureInfo.InvariantCulture,
out int theFinalInteger))
{
return modVersion;
}
return $"{modVersion[..startIndex]}{(theFinalInteger + 1).ToString(CultureInfo.InvariantCulture)}";
}
public XDocument ToXDocument()
{
XDocument doc = new XDocument();
XElement rootElement = new XElement("contentpackage");
void addRootAttribute<T>(string name, T value) where T : notnull
=> rootElement.Add(new XAttribute(name, value.ToString() ?? ""));
addRootAttribute("name", Name);
addRootAttribute("modversion", ModVersion);
addRootAttribute("corepackage", IsCore);
if (SteamWorkshopId != 0) { addRootAttribute("steamworkshopid", SteamWorkshopId); }
addRootAttribute("gameversion", GameMain.Version);
if (AltNames.Any()) { addRootAttribute("altnames", string.Join(",", AltNames)); }
if (ExpectedHash != null) { addRootAttribute("expectedhash", ExpectedHash.StringRepresentation); }
if (InstallTime != null) { addRootAttribute("installtime", ToolBox.Epoch.FromDateTime(InstallTime.Value)); }
files.ForEach(f => rootElement.Add(f.ToXElement()));
doc.Add(rootElement);
return doc;
}
public void Save(string path)
{
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
ToXDocument().SaveSafe(path);
}
}
}

View File

@@ -0,0 +1,61 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Barotrauma.IO;
using Barotrauma.Steam;
namespace Barotrauma
{
public static partial class ContentPackageManager
{
public sealed partial class PackageSource : ICollection<ContentPackage>
{
public string SaveRegularMod(ModProject modProject)
{
if (modProject.IsCore) { throw new ArgumentException("ModProject must not be a core package"); }
string fileListPath = Path.Combine(directory, ToolBox.RemoveInvalidFileNameChars(modProject.Name), ContentPackage.FileListFileName)
.CleanUpPathCrossPlatform(correctFilenameCase: false);
modProject.Save(fileListPath);
Refresh(); EnabledPackages.DisableRemovedMods();
return fileListPath;
}
public RegularPackage GetRegularModByPath(string fileListPath)
{
return Regular.First(p => p.Path == fileListPath);
}
public RegularPackage SaveAndEnableRegularMod(ModProject modProject)
{
string fileListPath = SaveRegularMod(modProject);
var package = GetRegularModByPath(fileListPath);
EnabledPackages.EnableRegular(package);
return package;
}
}
private static async Task<IEnumerable<Steamworks.Ugc.Item>> EnqueueWorkshopUpdates()
{
ISet<Steamworks.Ugc.Item> subscribedItems = await SteamManager.Workshop.GetAllSubscribedItems();
var needInstalling = subscribedItems.Where(item
=> !WorkshopPackages.Any(p
=> item.Id == p.SteamWorkshopId
&& p.InstallTime.HasValue
&& item.LatestUpdateTime <= p.InstallTime))
.ToArray();
if (needInstalling.Any())
{
await Task.WhenAll(
needInstalling.Select(SteamManager.Workshop.DownloadModThenEnqueueInstall));
}
return needInstalling;
}
}
}

View File

@@ -0,0 +1,378 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using Directory = Barotrauma.IO.Directory;
using File = Barotrauma.IO.File;
using Path = Barotrauma.IO.Path;
namespace Barotrauma.Transition
{
/// <summary>
/// Class dedicated to transitioning away from the old, shitty
/// Mods + Submarines folders to the new LocalMods folder
/// </summary>
public static class UgcTransition
{
private const string readmeName = "LOCALMODS_README.txt";
private enum ModsListChildType
{
Header,
Entry
}
public static void Prepare()
{
TaskPool.Add("UgcTransition.Prepare", DetermineItemsToTransition(), t =>
{
if (!t.TryGetResult(out (OldSubs, OldItemAssemblies, OldMods) result)) { return; }
var (subs, itemAssemblies, mods) = result;
if (!subs.FilePaths.Any() && !itemAssemblies.FilePaths.Any() && !mods.Mods.Any()) { return; }
var msgBox = new GUIMessageBox(TextManager.Get("Ugc.TransferTitle"), "", relativeSize: (0.5f, 0.8f),
buttons: new LocalizedString[] { TextManager.Get("Ugc.TransferButton") });
var closeBtn = new GUIButton(
new RectTransform(Vector2.One * 1.5f, msgBox.Header.RectTransform, anchor: Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight),
style: "GUICancelButton")
{
OnClicked = (button, o) =>
{
msgBox.Close();
return false;
}
};
var desc = new GUITextBlock(new RectTransform((1.0f, 0.24f), msgBox.Content.RectTransform),
text: TextManager.Get("Ugc.TransferDesc"), wrap: true, textAlignment: Alignment.CenterLeft);
var modsList = new GUIListBox(new RectTransform((1.0f, 0.6f), msgBox.Content.RectTransform))
{
HoverCursor = CursorState.Default
};
Dictionary<string, GUITickBox> pathTickboxMap = new Dictionary<string, GUITickBox>();
void addHeader(LocalizedString str)
{
var itemFrame = new GUIFrame(new RectTransform((1.0f, 0.08f), modsList.Content.RectTransform),
style: null)
{
CanBeFocused = false,
UserData = ModsListChildType.Header
};
if (str is RawLString { Value: "" }) { return; }
bool clicked = true;
var tickBox = new GUITickBox(new RectTransform(Vector2.One, itemFrame.RectTransform),
label: str, font: GUIStyle.SubHeadingFont)
{
Selected = false,
OnSelected = box =>
{
if (!clicked) { return true; }
bool toggleTickbox = false;
foreach (var child in modsList.Content.Children)
{
if (child == itemFrame) { toggleTickbox = true; }
else if (child.UserData is ModsListChildType.Header) { toggleTickbox = false; }
else if (toggleTickbox)
{
var tb = child.GetAnyChild<GUITickBox>();
if (tb is null) { continue; }
tb.Selected = box.Selected;
}
}
return true;
}
};
new GUICustomComponent(new RectTransform(Vector2.Zero, itemFrame.RectTransform),
onUpdate: (f, component) =>
{
clicked = false;
bool shouldBeSelected = true;
bool toggleTickbox = false;
foreach (var child in modsList.Content.Children)
{
if (child == itemFrame) { toggleTickbox = true; }
else if (child.UserData is ModsListChildType.Header) { toggleTickbox = false; }
else if (toggleTickbox)
{
var tb = child.GetAnyChild<GUITickBox>();
if (tb is null) { continue; }
if (!tb.Selected)
{
shouldBeSelected = false;
break;
}
}
}
tickBox.Selected = shouldBeSelected;
clicked = true;
});
}
void addTickbox(string dir, string name, bool ticked)
{
var itemFrame = new GUIFrame(new RectTransform((1.0f, 0.07f), modsList.Content.RectTransform),
style: null)
{
CanBeFocused = false,
UserData = ModsListChildType.Entry
};
var tickbox = new GUITickBox(new RectTransform((0.97f, 1.0f), itemFrame.RectTransform, Anchor.CenterRight), name)
{
Selected = ticked
};
pathTickboxMap.Add(dir, tickbox);
}
bool firstHeader = true;
void addSpacer()
{
if (firstHeader) { firstHeader = false; return; }
addHeader("");
}
if (subs.FilePaths.Any())
{
addSpacer();
addHeader(TextManager.Get("WorkshopLabelSubmarines"));
foreach (var sub in subs.FilePaths)
{
var subName = Path.GetFileNameWithoutExtension(sub);
addTickbox(sub, subName, ticked: !ContentPackageManager.LocalPackages.Any(p => p.NameMatches(subName)));
}
}
if (itemAssemblies.FilePaths.Any())
{
addSpacer();
addHeader(TextManager.Get("ItemAssemblies"));
foreach (var itemAssembly in itemAssemblies.FilePaths)
{
var assemblyName = Path.GetFileNameWithoutExtension(itemAssembly);
addTickbox(itemAssembly, assemblyName, ticked: !ContentPackageManager.LocalPackages.Any(p => p.NameMatches(assemblyName)));
}
}
if (mods.Mods.Any())
{
addSpacer();
addHeader(TextManager.Get("SubscribedMods"));
foreach (var mod in mods.Mods)
{
addTickbox(mod.Dir, mod.Name, ticked: !ContentPackageManager.LocalPackages.Any(p => p.SteamWorkshopId != 0 && p.SteamWorkshopId == mod.Item?.Id));
}
}
GUIMessageBox? subMsgBox = null;
void createSubMsgBox(LocalizedString str, bool closable)
{
subMsgBox?.Close();
subMsgBox = new GUIMessageBox(headerText: "", text: str,
buttons: closable ? new[] { TextManager.Get("Close") } : Array.Empty<LocalizedString>());
if (closable)
{
subMsgBox.Buttons[0].OnClicked = subMsgBox.Close;
}
}
msgBox.Buttons[0].OnClicked = (b, o) =>
{
TaskPool.Add("TransferMods", TransferMods(pathTickboxMap), t2 =>
{
if (t2.Exception != null)
{
DebugConsole.ThrowError("There was an error transferring mods", t2.Exception.GetInnermost());
}
ContentPackageManager.LocalPackages.Refresh();
if (t2.TryGetResult(out string[] modsToEnable))
{
var newRegular = ContentPackageManager.EnabledPackages.Regular.ToList();
newRegular.AddRange(ContentPackageManager.LocalPackages.Regular
.Where(r => modsToEnable.Contains(r.Dir.CleanUpPathCrossPlatform(correctFilenameCase: false))));
newRegular = newRegular.Distinct().ToList();
ContentPackageManager.EnabledPackages.SetRegular(newRegular);
}
createSubMsgBox(TextManager.Get("Ugc.TransferComplete"), closable: true);
});
msgBox.Close();
createSubMsgBox(TextManager.Get("Ugc.Transferring"), closable: false);
return false;
};
});
}
private struct OldSubs
{
public readonly IReadOnlyList<string> FilePaths;
public OldSubs(IReadOnlyList<string> filePaths)
{
FilePaths = filePaths;
}
}
private struct OldItemAssemblies
{
public readonly IReadOnlyList<string> FilePaths;
public OldItemAssemblies(IReadOnlyList<string> filePaths)
{
FilePaths = filePaths;
}
}
private struct OldMods
{
public readonly IReadOnlyList<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)> Mods;
public OldMods(IReadOnlyList<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)> mods)
{
Mods = mods;
}
}
private const string oldSubsPath = "Submarines";
private const string oldModsPath = "Mods";
private const string oldItemAssembliesPath = "ItemAssemblies";
private static async Task<(OldSubs Subs, OldItemAssemblies ItemAssemblies, OldMods Mods)> DetermineItemsToTransition()
{
string[] subs = Array.Empty<string>();
string[] itemAssemblies = Array.Empty<string>();
List<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)> mods
= new List<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)>();
if (FolderShouldBeTransitioned(oldModsPath))
{
string[] getFiles(string path, string pattern)
=> Directory.Exists(path)
? Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly)
: Array.Empty<string>();
subs = getFiles(oldSubsPath, "*.sub");
itemAssemblies = getFiles(oldItemAssembliesPath, "*.xml");
string[] allOldMods = Directory.GetDirectories(oldModsPath, "*", SearchOption.TopDirectoryOnly);
var publishedItems = await SteamManager.Workshop.GetPublishedItems();
foreach (var modDir in allOldMods)
{
var fileList = XMLExtensions.TryLoadXml(Path.Combine(modDir, ContentPackage.FileListFileName), out _);
if (fileList?.Root is null) { continue; }
var oldId = fileList.Root.GetAttributeUInt64("steamworkshopid", 0);
var updateTime = File.GetLastWriteTime(modDir).ToUniversalTime();
var oldName = fileList.Root.GetAttributeString("name", "");
var item = oldId != 0 ? publishedItems.FirstOrNull(it => it.Id == oldId) : null;
if (oldId == 0 || item.HasValue)
{
mods.Add((modDir, oldName, item, updateTime));
}
}
}
while (!(Screen.Selected is MainMenuScreen)) { await Task.Delay(50); }
return (new OldSubs(subs), new OldItemAssemblies(itemAssemblies), new OldMods(mods));
}
private static bool FolderShouldBeTransitioned(string folderName)
{
return Directory.Exists(folderName)
&& !File.Exists(Path.Combine(folderName, readmeName));
}
private static async Task<string[]> TransferMods(Dictionary<string, GUITickBox> pathTickboxMap)
{
//WriteReadme(oldSubsPath); //can't do this because the old submarine discovery code is borked
WriteReadme(oldModsPath);
var modsToEnable = (await Task.WhenAll(pathTickboxMap.Select(TransferMod))).OfType<string>().ToArray();
return modsToEnable;
}
private static Task<string?> TransferMod(KeyValuePair<string, GUITickBox> kvp)
=> TransferMod(kvp.Key, kvp.Value);
private static async Task<string?> TransferMod(string path, GUITickBox tickbox)
{
if (!tickbox.Selected) { return null; }
string dirName = Path.GetFileNameWithoutExtension(path);
string destPath = Path.Combine(ContentPackage.LocalModsDir, dirName);
//find unique path to save in
for (int i = 0;;i++)
{
if (!Directory.Exists(destPath)) { break; }
destPath = Path.Combine(ContentPackage.LocalModsDir, $"{dirName}.{i}");
}
bool isSub = path.StartsWith(oldSubsPath, StringComparison.OrdinalIgnoreCase);
bool isItemAssembly = path.StartsWith(oldItemAssembliesPath, StringComparison.OrdinalIgnoreCase);
if (isSub || isItemAssembly)
{
//copying a sub or item assembly: manually create filelist.xml
ModProject modProject = new ModProject
{
Name = dirName,
ModVersion = ContentPackage.DefaultModVersion
};
Type fileType;
if (isSub)
{
fileType = typeof(SubmarineFile);
XDocument? doc = SubmarineInfo.OpenFile(path, out _);
if (doc?.Root != null)
{
SubmarineType subType = doc.Root.GetAttributeEnum("type", SubmarineType.Player);
fileType = SubEditorScreen.DetermineSubFileType(subType);
}
}
else
{
fileType = typeof(ItemAssemblyFile);
}
modProject.AddFile(ModProject.File.FromPath(
Path.Combine(ContentPath.ModDirStr, $"{dirName}.{(isSub ? "sub" : "xml")}"),
fileType));
Directory.CreateDirectory(destPath);
File.Copy(path, Path.Combine(destPath, $"{dirName}.{(isSub ? "sub" : "xml")}"));
modProject.Save(Path.Combine(destPath, ContentPackage.FileListFileName));
return destPath.CleanUpPathCrossPlatform(correctFilenameCase: false);
}
else
{
//copying a mod: we have a neat method for that!
await SteamManager.Workshop.CopyDirectory(path, Path.GetFileName(path), path, destPath);
return null;
}
}
private static void WriteReadme(string folderName)
{
if (!Directory.Exists(folderName)) { return; }
File.WriteAllText(path: Path.Combine(folderName, readmeName),
contents: "This folder is no longer used by Barotrauma;\n" +
"your mods and submarines should have been transferred\n" +
"to LocalMods. If they are not being found, delete this\n" +
"readme and relaunch the game.", encoding: Encoding.UTF8);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -94,7 +94,7 @@ namespace Barotrauma
var (relative, min) = GetSizes(dialogType);
GUIMessageBox messageBox = new GUIMessageBox(string.Empty, string.Empty, new string[0],
GUIMessageBox messageBox = new GUIMessageBox(string.Empty, string.Empty, Array.Empty<LocalizedString>(),
relativeSize: relative, minSize: min,
type: GUIMessageBox.Type.InGame, backgroundIcon: EventSet.GetEventSprite(spriteIdentifier))
{
@@ -105,7 +105,7 @@ namespace Barotrauma
messageBox.InnerFrame.ClearChildren();
messageBox.AutoClose = false;
GUI.Style.Apply(messageBox.InnerFrame, "DialogBox");
GUIStyle.Apply(messageBox.InnerFrame, "DialogBox");
if (actionInstance != null)
{
@@ -222,11 +222,11 @@ namespace Barotrauma
closeButton.SlideIn(0.5f, 0.33f, 16, SlideDirection.Down);
InputType? closeInput = null;
if (GameMain.Config.KeyBind(InputType.Use).MouseButton == MouseButton.None)
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Use].MouseButton == MouseButton.None)
{
closeInput = InputType.Use;
}
else if (GameMain.Config.KeyBind(InputType.Select).MouseButton == MouseButton.None)
else if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select].MouseButton == MouseButton.None)
{
closeInput = InputType.Select;
}
@@ -239,7 +239,7 @@ namespace Barotrauma
{
GUIButton btn = component as GUIButton;
btn?.OnClicked(btn, btn.UserData);
btn?.Flash(GUI.Style.Green);
btn?.Flash(GUIStyle.Green);
}
};
}
@@ -308,7 +308,7 @@ namespace Barotrauma
AlwaysOverrideCursor = true
};
string translatedText = TextManager.Get(text, returnNull: true) ?? text;
LocalizedString translatedText = TextManager.Get(text);
if (speaker?.Info != null && drawChathead)
{
@@ -335,9 +335,9 @@ namespace Barotrauma
{
foreach (string option in options)
{
var btn = new GUIButton(new RectTransform(new Vector2(0.9f, 0.01f), textContent.RectTransform), TextManager.Get(option, returnNull: true) ?? option, style: "ListBoxElement");
var btn = new GUIButton(new RectTransform(new Vector2(0.9f, 0.01f), textContent.RectTransform), TextManager.Get(option), style: "ListBoxElement");
btn.TextBlock.TextAlignment = Alignment.CenterLeft;
btn.TextColor = btn.HoverTextColor = GUI.Style.Green;
btn.TextColor = btn.HoverTextColor = GUIStyle.Green;
btn.TextBlock.Wrap = true;
buttons.Add(btn);
}
@@ -384,7 +384,7 @@ namespace Barotrauma
}
// Too broken, left it here if I ever want to come back to it
private static List<RichTextData> GetQuoteHighlights(string text, Color color)
/*private static List<RichTextData> GetQuoteHighlights(string text, Color color)
{
char[] quotes = { '“', '”', '\"', '\'', '「', '」'};
@@ -406,6 +406,6 @@ namespace Barotrauma
last.EndIndex = text.Length;
}
return textColors;
}
}*/
}
}

View File

@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
@@ -34,11 +35,11 @@ namespace Barotrauma
var textOffset = new Vector2(-150, 0);
spriteBatch.DrawCircle(drawPos, 600, 6, Color.White, thickness: 20);
GUI.DrawString(spriteBatch, drawPos + textOffset, ev.ToString(), Color.White, Color.Black, 0, GUI.LargeFont);
GUI.DrawString(spriteBatch, drawPos + textOffset, ev.ToString(), Color.White, Color.Black, 0, GUIStyle.LargeFont);
}
}
public void DebugDrawHUD(SpriteBatch spriteBatch, int y)
public void DebugDrawHUD(SpriteBatch spriteBatch, float y)
{
foreach (ScriptedEvent scriptedEvent in activeEvents.Where(ev => !ev.IsFinished && ev is ScriptedEvent).Cast<ScriptedEvent>())
{
@@ -46,24 +47,29 @@ namespace Barotrauma
}
float theoreticalMaxMonsterStrength = 10000;
float relativeMaxMonsterStrength = theoreticalMaxMonsterStrength * GameMain.GameSession.LevelData.Difficulty / 100;
float relativeMaxMonsterStrength = theoreticalMaxMonsterStrength * (GameMain.GameSession?.LevelData?.Difficulty ?? 0f) / 100;
float absoluteMonsterStrength = monsterStrength / theoreticalMaxMonsterStrength;
float relativeMonsterStrength = monsterStrength / relativeMaxMonsterStrength;
GUI.DrawString(spriteBatch, new Vector2(10, y), "EventManager", Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 20), "Event cooldown: " + (int)Math.Max(eventCoolDown, 0), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 35), "Current intensity: " + (int)Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, currentIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 50), "Target intensity: " + (int)Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, targetIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 65), "Crew health: " + (int)Math.Round(avgCrewHealth * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgCrewHealth), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 80), "Hull integrity: " + (int)Math.Round(avgHullIntegrity * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgHullIntegrity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 95), "Flooding amount: " + (int)Math.Round(floodingAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, floodingAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 110), "Fire amount: " + (int)Math.Round(fireAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, fireAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 125), "Enemy danger: " + (int)Math.Round(enemyDanger * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, enemyDanger), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 140), "Current monster strength (total): " + (int)Math.Round(monsterStrength), Color.Lerp(GUI.Style.Green, GUI.Style.Red, relativeMonsterStrength), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 155), "Main events: " + (int)Math.Round(CumulativeMonsterStrengthMain), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 170), "Ruin events: " + (int)Math.Round(CumulativeMonsterStrengthRuins), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 185), "Wreck events: " + (int)Math.Round(CumulativeMonsterStrengthWrecks), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 200), "Cave events: " + (int)Math.Round(CumulativeMonsterStrengthCaves), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(10, y), "EventManager", Color.White, backgroundColor: Color.Black * 0.6f, font: GUIStyle.SmallFont);
DrawString("Event cooldown", Math.Max(eventCoolDown, 0), Color.White, spacing: 20);
DrawString("Current intensity", Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUIStyle.Red, currentIntensity));
DrawString("Target intensity", Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUIStyle.Red, targetIntensity));
DrawString("Crew health", Math.Round(avgCrewHealth * 100), Color.Lerp(GUIStyle.Red, GUIStyle.Green, avgCrewHealth));
DrawString("Hull integrity", Math.Round(avgHullIntegrity * 100), Color.Lerp(GUIStyle.Red, GUIStyle.Green, avgHullIntegrity));
DrawString("Flooding amount", Math.Round(floodingAmount * 100), Color.Lerp(GUIStyle.Green, GUIStyle.Red, floodingAmount));
DrawString("Fire amount", Math.Round(fireAmount * 100), Color.Lerp(GUIStyle.Green, GUIStyle.Red, fireAmount));
DrawString("Enemy danger", Math.Round(enemyDanger * 100), Color.Lerp(GUIStyle.Green, GUIStyle.Red, enemyDanger));
DrawString("Current monster strength (total)", Math.Round(monsterStrength), Color.Lerp(GUIStyle.Green, GUIStyle.Red, relativeMonsterStrength));
DrawString("Main events", Math.Round(CumulativeMonsterStrengthMain), Color.White);
DrawString("Ruin events", Math.Round(CumulativeMonsterStrengthRuins), Color.White);
DrawString("Wreck events", Math.Round(CumulativeMonsterStrengthWrecks), Color.White);
void DrawString(string text, double value, Color textColor, int spacing = 15)
{
y += GUI.AdjustForTextScale(spacing);
GUI.DrawString(spriteBatch, new Vector2(15, y), $"{text}: {(int)value}", textColor, backgroundColor: Color.Black * 0.6f, font: GUIStyle.SmallFont);
}
#if DEBUG
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) &&
@@ -89,7 +95,7 @@ namespace Barotrauma
lastIntensityUpdate = (float)Timing.TotalTime;
}
Rectangle graphRect = new Rectangle(15, y + 240, (int)(200 * GUI.xScale), (int)(100 * GUI.yScale));
Rectangle graphRect = new Rectangle(15, (int)(y + GUI.AdjustForTextScale(55)), (int)(200 * GUI.xScale), (int)(100 * GUI.yScale));
bool isGraphHovered = graphRect.Contains(PlayerInput.MousePosition);
bool leftMousePressed = PlayerInput.PrimaryMouseButtonDown() || PlayerInput.PrimaryMouseButtonHeld();
bool rightMousePressed = PlayerInput.SecondaryMouseButtonHeld() || PlayerInput.SecondaryMouseButtonDown();
@@ -101,10 +107,12 @@ namespace Barotrauma
{
isGraphSelected = false;
}
Color intensityColor = Color.Lerp(Color.White, GUI.Style.Red, currentIntensity);
Color intensityColor = Color.Lerp(Color.White, GUIStyle.Red, currentIntensity);
if (isGraphHovered || isGraphSelected)
{
graphRect.Size = new Point(GameMain.GraphicsWidth - 30, (int)(GameMain.GraphicsHeight * 0.35f));
int padding = 15;
int graphHeight = Math.Min((int)(GameMain.GraphicsHeight * 0.35f), GameMain.GraphicsHeight - (graphRect.Top + 3 * padding));
graphRect.Size = new Point(GameMain.GraphicsWidth - 2 * padding, graphHeight);
intensityColor = Color.Red;
GUI.DrawRectangle(spriteBatch, graphRect, Color.Black * 0.95f, isFilled: true);
}
@@ -122,7 +130,7 @@ namespace Barotrauma
{
height *= 3;
string text = (order / 6).ToString();
var font = GUI.SmallFont;
var font = GUIStyle.SmallFont;
Vector2 textSize = font.MeasureString(text);
Vector2 textPos = new Vector2(bottomPoint.X - textSize.X / 2, bottomPoint.Y + height * 1.5f);
GUI.DrawString(sBatch, textPos, text, Color.White, font: font);
@@ -173,74 +181,76 @@ namespace Barotrauma
y += yStep;
}
int x = graphRect.X;
float adjustedYStep = GUI.AdjustForTextScale(15);
if (isCrewAway && crewAwayDuration < settings.FreezeDurationWhenCrewAway)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), "Events frozen (crew away from sub): " + ToolBox.SecondsToReadableTime(settings.FreezeDurationWhenCrewAway - crewAwayDuration), Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15;
GUI.DrawString(spriteBatch, new Vector2(x, y), "Events frozen (crew away from sub): " + ToolBox.SecondsToReadableTime(settings.FreezeDurationWhenCrewAway - crewAwayDuration), Color.LightGreen * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
else if (crewAwayResetTimer > 0.0f)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), "Events frozen (crew just returned to the sub): " + ToolBox.SecondsToReadableTime(crewAwayResetTimer), Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15;
GUI.DrawString(spriteBatch, new Vector2(x, y), "Events frozen (crew just returned to the sub): " + ToolBox.SecondsToReadableTime(crewAwayResetTimer), Color.LightGreen * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
else if (eventCoolDown > 0.0f)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), "Event cooldown active: " + ToolBox.SecondsToReadableTime(eventCoolDown), Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15;
GUI.DrawString(spriteBatch, new Vector2(x, y), "Event cooldown active: " + ToolBox.SecondsToReadableTime(eventCoolDown), Color.LightGreen * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
else if (currentIntensity > eventThreshold)
{
GUI.DrawString(spriteBatch, new Vector2(x, y),
"Intensity too high for new events: " + (int)(currentIntensity * 100) + "%/" + (int)(eventThreshold * 100) + "%", Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15;
"Intensity too high for new events: " + (int)(currentIntensity * 100) + "%/" + (int)(eventThreshold * 100) + "%", Color.LightGreen * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
adjustedYStep = GUI.AdjustForTextScale(12);
foreach (EventSet eventSet in pendingEventSets)
{
if (Submarine.MainSub == null) { break; }
GUI.DrawString(spriteBatch, new Vector2(x, y), "New event (ID " + eventSet.DebugIdentifier + ") after: ", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
GUI.DrawString(spriteBatch, new Vector2(x, y), "New event (ID " + eventSet.Identifier + ") after: ", Color.Orange * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
if (eventSet.PerCave)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near cave", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near cave", Color.Orange * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
if (eventSet.PerWreck)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near the wreck", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near the wreck", Color.Orange * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
if (eventSet.PerRuin)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near the ruins", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near the ruins", Color.Orange * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
if (roundDuration < eventSet.MinMissionTime)
{
GUI.DrawString(spriteBatch, new Vector2(x, y),
" " + (int) (eventSet.MinDistanceTraveled * 100.0f) + "% travelled (current: " + (int) (distanceTraveled * 100.0f) + " %)",
((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) ? Color.Lerp(GUI.Style.Yellow, GUI.Style.Red, eventSet.MinDistanceTraveled - distanceTraveled) : GUI.Style.Green) * 0.8f, null, 0, GUI.SmallFont);
y += 12;
((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) ? Color.Lerp(GUIStyle.Yellow, GUIStyle.Red, eventSet.MinDistanceTraveled - distanceTraveled) : GUIStyle.Green) * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity)
{
GUI.DrawString(spriteBatch, new Vector2(x, y),
" intensity between " + eventSet.MinIntensity.FormatDoubleDecimal() + " and " + eventSet.MaxIntensity.FormatDoubleDecimal(),
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
Color.Orange * 0.8f, null, 0, GUIStyle.SmallFont);
y += adjustedYStep;
}
if (roundDuration < eventSet.MinMissionTime)
{
GUI.DrawString(spriteBatch, new Vector2(x, y),
" " + (int) (eventSet.MinMissionTime - roundDuration) + " s",
Color.Lerp(GUI.Style.Yellow, GUI.Style.Red, (eventSet.MinMissionTime - roundDuration)), null, 0, GUI.SmallFont);
Color.Lerp(GUIStyle.Yellow, GUIStyle.Red, (eventSet.MinMissionTime - roundDuration)), null, 0, GUIStyle.SmallFont);
}
y += 15;
y += GUI.AdjustForTextScale(15);
if (y > GameMain.GraphicsHeight * 0.9f)
{
@@ -249,14 +259,15 @@ namespace Barotrauma
}
}
GUI.DrawString(spriteBatch, new Vector2(x, y), "Current events: ", Color.White * 0.9f, null, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(x, y), "Current events: ", Color.White * 0.9f, null, 0, GUIStyle.SmallFont);
y += yStep;
adjustedYStep = GUI.AdjustForTextScale(18);
foreach (Event ev in activeEvents.Where(ev => !ev.IsFinished || PlayerInput.IsShiftDown()))
{
GUI.DrawString(spriteBatch, new Vector2(x + 5, y), ev.ToString(), (!ev.IsFinished ? Color.White : Color.Red) * 0.8f, null, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(x + 5, y), ev.ToString(), (!ev.IsFinished ? Color.White : Color.Red) * 0.8f, null, 0, GUIStyle.SmallFont);
Rectangle rect = new Rectangle(new Point(x + 5, y), GUI.SmallFont.MeasureString(ev.ToString()).ToPoint());
Rectangle rect = new Rectangle(new Point(x + 5, (int)y), GUIStyle.SmallFont.MeasureString(ev.ToString()).ToPoint());
Rectangle outlineRect = new Rectangle(rect.Location, rect.Size);
outlineRect.Inflate(4, 4);
@@ -281,7 +292,7 @@ namespace Barotrauma
}
}
y += 18;
y += adjustedYStep;
if (y > GameMain.GraphicsHeight * 0.9f)
{
y = graphRect.Bottom + yStep * 2;
@@ -331,8 +342,8 @@ namespace Barotrauma
if (Screen.Selected is GameScreen screen)
{
Camera cam = screen.Cam;
Dictionary<Entity, List<string>> tagsDictionary = new Dictionary<Entity, List<string>>();
foreach ((string key, List<Entity> value) in scriptedEvent.Targets)
Dictionary<Entity, List<Identifier>> tagsDictionary = new Dictionary<Entity, List<Identifier>>();
foreach ((Identifier key, List<Entity> value) in scriptedEvent.Targets)
{
foreach (Entity entity in value)
{
@@ -342,24 +353,24 @@ namespace Barotrauma
}
else
{
tagsDictionary.Add(entity, new List<string> { key });
tagsDictionary.Add(entity, new List<Identifier> { key });
}
}
}
string identifier = scriptedEvent.Prefab.Identifier;
Identifier identifier = scriptedEvent.Prefab.Identifier;
foreach ((Entity entity, List<string> tags) in tagsDictionary)
foreach ((Entity entity, List<Identifier> tags) in tagsDictionary)
{
if (entity.Removed) { continue; }
string text = tags.Aggregate("Tags:\n", (current, tag) => current + $" {tag.ColorizeObject()}\n").TrimEnd('\r', '\n');
if (!string.IsNullOrWhiteSpace(identifier)) { text = $"Event: {identifier.ColorizeObject()}\n{text}"; }
if (!identifier.IsEmpty) { text = $"Event: {identifier.ColorizeObject()}\n{text}"; }
List<RichTextData> richTextData = RichTextData.GetRichTextData(text, out text);
ImmutableArray<RichTextData>? richTextData = RichTextData.GetRichTextData(text, out text);
Vector2 entityPos = cam.WorldToScreen(entity.WorldPosition);
Vector2 infoSize = GUI.SmallFont.MeasureString(text);
Vector2 infoSize = GUIStyle.SmallFont.MeasureString(text);
Vector2 infoPos = entityPos + new Vector2(128 * cam.Zoom, -(128 * cam.Zoom));
infoPos.Y -= infoSize.Y / 2;
@@ -370,7 +381,7 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, infoRect, Color.Black * 0.8f, isFilled: true);
GUI.DrawRectangle(spriteBatch, infoRect, Color.White, isFilled: false);
GUI.DrawStringWithColors(spriteBatch, infoPos, text, Color.White, richTextData, font: GUI.SmallFont);
GUI.DrawStringWithColors(spriteBatch, infoPos, text, Color.White, richTextData, font: GUIStyle.SmallFont);
GUI.DrawLine(spriteBatch, entityPos, new Vector2(infoRect.Location.X, infoRect.Location.Y + infoRect.Height / 2), Color.White);
}
@@ -489,15 +500,15 @@ namespace Barotrauma
{
text = text.TrimEnd('\r', '\n');
string identifier = @event.Prefab.Identifier;
if (!string.IsNullOrWhiteSpace(identifier))
Identifier identifier = @event.Prefab.Identifier;
if (!identifier.IsEmpty)
{
text = $"Identifier: {identifier.ColorizeObject()}\n{text}";
}
List<RichTextData> richTextData = RichTextData.GetRichTextData(text, out text);
ImmutableArray<RichTextData>? richTextData = RichTextData.GetRichTextData(text, out text);
Vector2 size = GUI.SmallFont.MeasureString(text);
Vector2 size = GUIStyle.SmallFont.MeasureString(text);
Vector2 pos = pinnedPosition;
Rectangle infoRect;
Rectangle? infoBarRect = null;
@@ -520,7 +531,7 @@ namespace Barotrauma
const string titleHeader = "Pinned event";
GUI.DrawRectangle(spriteBatch, barRect, Color.DarkGray * 0.8f, isFilled: true);
GUI.DrawString(spriteBatch, barRect.Location.ToVector2() + barRect.Size.ToVector2() / 2 - GUI.SubHeadingFont.MeasureString(titleHeader) / 2, titleHeader, Color.White);
GUI.DrawString(spriteBatch, barRect.Location.ToVector2() + barRect.Size.ToVector2() / 2 - GUIStyle.SubHeadingFont.MeasureString(titleHeader) / 2, titleHeader, Color.White);
GUI.DrawRectangle(spriteBatch, barRect, Color.White);
infoBarRect = barRect;
}
@@ -546,8 +557,14 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, infoRect, Color.Black * 0.8f, isFilled: true);
GUI.DrawRectangle(spriteBatch, infoRect, Color.White);
GUI.DrawStringWithColors(spriteBatch, pos, text, Color.White, richTextData, null, 0, GUI.SmallFont);
richTextData.Clear();
if (richTextData.HasValue && richTextData.Value.Length > 0)
{
GUI.DrawStringWithColors(spriteBatch, pos, text, Color.White, richTextData.Value, null, 0, GUIStyle.SmallFont);
}
else
{
GUI.DrawString(spriteBatch, pos, text, Color.White, null, 0, GUIStyle.SmallFont);
}
return infoBarRect ?? infoRect;
}
@@ -557,7 +574,7 @@ namespace Barotrauma
switch (eventType)
{
case NetworkEventType.STATUSEFFECT:
string eventIdentifier = msg.ReadString();
Identifier eventIdentifier = msg.ReadIdentifier();
UInt16 actionIndex = msg.ReadUInt16();
UInt16 targetCount = msg.ReadUInt16();
List<Entity> targets = new List<Entity>();
@@ -571,14 +588,14 @@ namespace Barotrauma
var eventPrefab = EventSet.GetEventPrefab(eventIdentifier);
if (eventPrefab == null) { return; }
int j = 0;
foreach (XElement element in eventPrefab.ConfigElement.Descendants())
foreach (var element in eventPrefab.ConfigElement.Descendants())
{
if (j != actionIndex)
{
j++;
continue;
}
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase)) { continue; }
StatusEffect effect = StatusEffect.Load(subElement, $"EventManager.ClientRead ({eventIdentifier})");
@@ -633,13 +650,13 @@ namespace Barotrauma
}
break;
case NetworkEventType.MISSION:
string missionIdentifier = msg.ReadString();
Identifier missionIdentifier = msg.ReadIdentifier();
MissionPrefab? prefab = MissionPrefab.List.Find(mp => mp.Identifier.Equals(missionIdentifier, StringComparison.OrdinalIgnoreCase));
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
if (prefab != null)
{
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", prefab.Name),
new string[0], 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
};
@@ -657,7 +674,7 @@ namespace Barotrauma
{
GameMain.GameSession.Map.Connections[connectionIndex].Locked = false;
new GUIMessageBox(string.Empty, TextManager.Get("pathunlockedgeneric"),
new string[0], type: GUIMessageBox.Type.InGame, iconStyle: "UnlockPathIcon", relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128));
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, iconStyle: "UnlockPathIcon", relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128));
}
}
break;

View File

@@ -13,7 +13,7 @@ namespace Barotrauma
if (state != value)
{
base.State = value;
if (state == HostagesKilledState && !string.IsNullOrEmpty(hostagesKilledMessage))
if (state == HostagesKilledState && !hostagesKilledMessage.IsNullOrEmpty())
{
CreateMessageBox(string.Empty, hostagesKilledMessage);
}

View File

@@ -8,23 +8,29 @@ namespace Barotrauma
public override bool DisplayAsCompleted => false;
public override bool DisplayAsFailed => false;
public override string GetMissionRewardText(Submarine sub)
public override RichString GetMissionRewardText(Submarine sub)
{
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub)));
LocalizedString rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub)));
LocalizedString retVal;
if (rewardPerCrate.HasValue)
{
string rewardPerCrateText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", rewardPerCrate.Value));
return TextManager.GetWithVariables("missionrewardcargopercrate",
new string[] { "[rewardpercrate]", "[itemcount]", "[maxitemcount]", "[totalreward]" },
new string[] { rewardPerCrateText, itemsToSpawn.Count.ToString(), maxItemCount.ToString(), $"‖color:gui.orange‖{rewardText}‖end‖" });
LocalizedString rewardPerCrateText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", rewardPerCrate.Value));
retVal = TextManager.GetWithVariables("missionrewardcargopercrate",
("[rewardpercrate]", rewardPerCrateText),
("[itemcount]", itemsToSpawn.Count.ToString()),
("[maxitemcount]", maxItemCount.ToString()),
("[totalreward]", $"‖color:gui.orange‖{rewardText}‖end‖"));
}
else
{
return TextManager.GetWithVariables("missionrewardcargo",
new string[] { "[totalreward]", "[itemcount]", "[maxitemcount]" },
new string[] { $"‖color:gui.orange‖{rewardText}‖end‖", itemsToSpawn.Count.ToString(), maxItemCount.ToString() });
retVal = TextManager.GetWithVariables("missionrewardcargo",
("[totalreward]", $"‖color:gui.orange‖{rewardText}‖end‖"),
("[itemcount]", itemsToSpawn.Count.ToString()),
("[maxitemcount]", maxItemCount.ToString()));
}
return RichString.Rich(retVal);
}
public override void ClientReadInitial(IReadMessage msg)
{

View File

@@ -4,7 +4,7 @@ namespace Barotrauma
{
partial class CombatMission : Mission
{
public override string Description
public override LocalizedString Description
{
get
{

View File

@@ -56,7 +56,7 @@ namespace Barotrauma
for(int i = 0; i < resourceClusters.Count; i++)
{
var identifier = msg.ReadString();
var identifier = msg.ReadIdentifier();
var count = msg.ReadByte();
var resources = new Item[count];
for (int j = 0; j < count; j++)

View File

@@ -9,11 +9,8 @@ namespace Barotrauma
{
abstract partial class Mission
{
private readonly List<string> shownMessages = new List<string>();
public IEnumerable<string> ShownMessages
{
get { return shownMessages; }
}
private readonly List<LocalizedString> shownMessages = new List<LocalizedString>();
public IEnumerable<LocalizedString> ShownMessages => shownMessages;
public bool DisplayTargetHudIcons => Prefab.DisplayTargetHudIcons;
@@ -32,29 +29,29 @@ namespace Barotrauma
{
int v = Difficulty ?? MissionPrefab.MinDifficulty;
float t = MathUtils.InverseLerp(MissionPrefab.MinDifficulty, MissionPrefab.MaxDifficulty, v);
return ToolBox.GradientLerp(t, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
return ToolBox.GradientLerp(t, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red);
}
public virtual string GetMissionRewardText(Submarine sub)
public virtual RichString GetMissionRewardText(Submarine sub)
{
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub)));
return TextManager.GetWithVariable("missionreward", "[reward]", $"‖color:gui.orange‖{rewardText}‖end‖");
LocalizedString rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub)));
return RichString.Rich(TextManager.GetWithVariable("missionreward", "[reward]", "‖color:gui.orange‖"+rewardText+"‖end‖"));
}
public string GetReputationRewardText(Location currLocation)
public RichString GetReputationRewardText(Location currLocation)
{
List<string> reputationRewardTexts = new List<string>();
List<LocalizedString> reputationRewardTexts = new List<LocalizedString>();
foreach (var reputationReward in ReputationRewards)
{
string name = "";
LocalizedString name = "";
if (reputationReward.Key.Equals("location", StringComparison.OrdinalIgnoreCase))
if (reputationReward.Key == "location")
{
name = $"‖color:gui.orange‖{currLocation.Name}‖end‖";
}
else
{
var faction = FactionPrefab.Prefabs.Find(f => f.Identifier.Equals(reputationReward.Key, StringComparison.OrdinalIgnoreCase));
var faction = FactionPrefab.Prefabs.Find(f => f.Identifier == reputationReward.Key);
if (faction != null)
{
name = $"‖color:{XMLExtensions.ColorToString(faction.IconColor)}‖{faction.Name}‖end‖";
@@ -66,28 +63,28 @@ namespace Barotrauma
}
float normalizedValue = MathUtils.InverseLerp(-100.0f, 100.0f, reputationReward.Value);
string formattedValue = ((int)reputationReward.Value).ToString("+#;-#;0"); //force plus sign for positive numbers
string rewardText = TextManager.GetWithVariables(
LocalizedString rewardText = TextManager.GetWithVariables(
"reputationformat",
new string[] { "[reputationname]", "[reputationvalue]" },
new string[] { name, $"‖color:{XMLExtensions.ColorToString(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖" });
reputationRewardTexts.Add(rewardText);
("[reputationname]", name),
("[reputationvalue]", $"‖color:{XMLExtensions.ColorToString(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖" ));
reputationRewardTexts.Add(rewardText.Value);
}
return TextManager.AddPunctuation(':', TextManager.Get("reputation"), string.Join(", ", reputationRewardTexts));
return RichString.Rich(TextManager.AddPunctuation(':', TextManager.Get("reputation"), LocalizedString.Join(", ", reputationRewardTexts)));
}
partial void ShowMessageProjSpecific(int missionState)
{
int messageIndex = missionState - 1;
if (messageIndex >= Headers.Count && messageIndex >= Messages.Count) { return; }
if (messageIndex >= Headers.Length && messageIndex >= Messages.Length) { return; }
if (messageIndex < 0) { return; }
string header = messageIndex < Headers.Count ? Headers[messageIndex] : "";
string message = messageIndex < Messages.Count ? Messages[messageIndex] : "";
LocalizedString header = messageIndex < Headers.Length ? Headers[messageIndex] : "";
LocalizedString message = messageIndex < Messages.Length ? Messages[messageIndex] : "";
CoroutineManager.StartCoroutine(ShowMessageBoxAfterRoundSummary(header, message));
}
private IEnumerable<CoroutineStatus> ShowMessageBoxAfterRoundSummary(string header, string message)
private IEnumerable<CoroutineStatus> ShowMessageBoxAfterRoundSummary(LocalizedString header, LocalizedString message)
{
while (GUIMessageBox.VisibleBox?.UserData is RoundSummary)
{
@@ -97,10 +94,10 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
protected void CreateMessageBox(string header, string message)
protected void CreateMessageBox(LocalizedString header, LocalizedString message)
{
shownMessages.Add(message);
new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon, parseRichText: true)
new GUIMessageBox(RichString.Rich(header), RichString.Rich(message), buttons: Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: Prefab.Icon)
{
IconColor = Prefab.IconColor
};

View File

@@ -1,4 +1,6 @@
namespace Barotrauma
using System;
namespace Barotrauma
{
abstract partial class MissionMode : GameMode
{
@@ -6,7 +8,7 @@
{
foreach (Mission mission in missions)
{
new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon, parseRichText: true)
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,
UserData = "missionstartmessage"

View File

@@ -4,7 +4,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
partial class MissionPrefab
partial class MissionPrefab : PrefabWithUintIdentifier
{
public Sprite Icon
{
@@ -49,11 +49,11 @@ namespace Barotrauma
private Sprite hudIcon;
private Color? hudIconColor;
partial void InitProjSpecific(XElement element)
partial void InitProjSpecific(ContentXElement element)
{
DisplayTargetHudIcons = element.GetAttributeBool("displaytargethudicons", false);
HudIconMaxDistance = element.GetAttributeFloat("hudiconmaxdistance", 1000.0f);
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
string name = subElement.Name.ToString();
if (name.Equals("icon", StringComparison.OrdinalIgnoreCase))
@@ -68,5 +68,10 @@ namespace Barotrauma
}
}
}
partial void DisposeProjectSpecific()
{
Icon?.Remove();
}
}
}

View File

@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
using SharpFont;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
@@ -19,7 +20,6 @@ namespace Barotrauma
private Face face;
private uint size;
private int baseHeight;
//private int lineHeight;
private Dictionary<uint, GlyphData> texCoords;
private List<Texture2D> textures;
private GraphicsDevice graphicsDevice;
@@ -53,6 +53,8 @@ namespace Barotrauma
}
}
public bool ForceUpperCase = false;
public float LineHeight => baseHeight * 1.8f;
private uint[] charRanges;
@@ -79,9 +81,9 @@ namespace Barotrauma
}
}
public ScalableFont(XElement element, GraphicsDevice gd = null)
public ScalableFont(ContentXElement element, GraphicsDevice gd = null)
: this(
element.GetAttributeString("file", ""),
element.GetAttributeContentPath("file")?.Value,
(uint)element.GetAttributeInt("size", 14),
gd,
element.GetAttributeBool("dynamicloading", false),
@@ -104,10 +106,7 @@ namespace Barotrauma
break;
}
}
if (this.face == null)
{
this.face = new Face(Lib, filename);
}
this.face ??= new Face(Lib, filename);
this.size = size;
this.textures = new List<Texture2D>();
this.texCoords = new Dictionary<uint, GlyphData>();
@@ -186,7 +185,7 @@ namespace Barotrauma
GlyphData blankData = new GlyphData(
advance: (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(j, blankData);
}
continue;
@@ -399,6 +398,52 @@ namespace Barotrauma
}
}
// TODO: refactor this further
private void HandleNewLineAndAlignment(
string text,
in Vector2 advanceUnit,
in Vector2 position,
in Vector2 scale,
Alignment alignment,
int i,
ref float lineWidth,
ref Vector2 currentLineOffset,
ref int lineNum,
ref Vector2 currentPos,
out uint charIndex,
out bool shouldContinue)
{
if ((alignment.HasFlag(Alignment.CenterX) || alignment.HasFlag(Alignment.Right)) && (lineWidth < 0.0f || text[i] == '\n'))
{
int startIndex = lineWidth < 0.0f ? i : (i + 1);
lineWidth = 0.0f;
for (int j = startIndex; j < text.Length; j++)
{
if (text[j] == '\n') { break; }
uint chrIndex = text[j];
var gd2 = GetGlyphData(chrIndex);
lineWidth += gd2.Advance;
}
currentLineOffset = -lineWidth * advanceUnit * scale.X;
if (alignment.HasFlag(Alignment.CenterX)) { currentLineOffset *= 0.5f; }
currentLineOffset.X = MathF.Round(currentLineOffset.X);
currentLineOffset.Y = MathF.Round(currentLineOffset.Y);
}
if (text[i] == '\n')
{
lineNum++;
currentPos = position;
currentPos.X -= LineHeight * lineNum * advanceUnit.Y * scale.Y;
currentPos.Y += LineHeight * lineNum * advanceUnit.X * scale.Y;
shouldContinue = true; charIndex = 0; return;
}
shouldContinue = false;
charIndex = text[i];
}
private GlyphData GetGlyphData(uint charIndex)
{
const uint DEFAULT_INDEX = 0x25A1; //U+25A1 = white square
@@ -412,29 +457,27 @@ namespace Barotrauma
return new GlyphData(texIndex: -1);
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
text = ApplyUpperCase(text, forceUpperCase);
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
float lineWidth = -1.0f;
Vector2 currentLineOffset = Vector2.Zero;
int lineNum = 0;
Vector2 currentPos = position;
Vector2 advanceUnit = rotation == 0.0f ? Vector2.UnitX : new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
{
lineNum++;
currentPos = position;
currentPos.X -= LineHeight * lineNum * advanceUnit.Y * scale.Y;
currentPos.Y += LineHeight * lineNum * advanceUnit.X * scale.Y;
continue;
}
uint charIndex = text[i];
HandleNewLineAndAlignment(text, advanceUnit, position, scale, alignment, i,
ref lineWidth, ref currentLineOffset, ref lineNum, ref currentPos,
out uint charIndex, out bool shouldContinue);
if (shouldContinue) { continue; }
GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
@@ -444,20 +487,30 @@ namespace Barotrauma
drawOffset.X = gd.DrawOffset.X * advanceUnit.X * scale.X - gd.DrawOffset.Y * advanceUnit.Y * scale.Y;
drawOffset.Y = gd.DrawOffset.X * advanceUnit.Y * scale.Y + gd.DrawOffset.Y * advanceUnit.X * scale.X;
sb.Draw(tex, currentPos + drawOffset, gd.TexCoords, color, rotation, origin, scale, se, layerDepth);
sb.Draw(tex, currentPos + currentLineOffset + drawOffset, gd.TexCoords, color, rotation, origin, scale, se, layerDepth);
}
currentPos += gd.Advance * advanceUnit * scale.X;
}
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth)
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
{
DrawString(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth);
DrawString(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, alignment, forceUpperCase);
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color)
private string ApplyUpperCase(string text, ForceUpperCase forceUpperCase)
=> forceUpperCase switch
{
Barotrauma.ForceUpperCase.Inherit => ForceUpperCase ? text.ToUpperInvariant() : text,
Barotrauma.ForceUpperCase.Yes => text.ToUpperInvariant(),
Barotrauma.ForceUpperCase.No => text
};
private readonly static VertexPositionColorTexture[] quadVertices = new VertexPositionColorTexture[4];
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit, bool italics = false)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
text = ApplyUpperCase(text, forceUpperCase);
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
@@ -478,21 +531,48 @@ namespace Barotrauma
GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
{
float halfCharHeight = gd.TexCoords.Height * 0.5f;
float slantStrength = 0.35f;
float topItalicOffset = italics ? ((halfCharHeight - gd.DrawOffset.Y) * slantStrength) + baseHeight * 0.18f : 0.0f;
float bottomItalicOffset = italics ? ((-halfCharHeight - gd.DrawOffset.Y) * slantStrength) + baseHeight * 0.18f : 0.0f;
Texture2D tex = textures[gd.TexIndex];
sb.Draw(tex, currentPos + gd.DrawOffset, gd.TexCoords, color);
quadVertices[0].Position = new Vector3(currentPos + gd.DrawOffset + (bottomItalicOffset, gd.TexCoords.Height), 0.0f);
quadVertices[0].TextureCoordinate = ((float)gd.TexCoords.Left / tex.Width, (float)gd.TexCoords.Bottom / tex.Height);
quadVertices[0].Color = color;
quadVertices[1].Position = new Vector3(currentPos + gd.DrawOffset + (topItalicOffset, 0.0f), 0.0f);
quadVertices[1].TextureCoordinate = ((float)gd.TexCoords.Left / tex.Width, (float)gd.TexCoords.Top / tex.Height);
quadVertices[1].Color = color;
quadVertices[2].Position = new Vector3(currentPos + gd.DrawOffset + (gd.TexCoords.Width + bottomItalicOffset, gd.TexCoords.Height), 0.0f);
quadVertices[2].TextureCoordinate = ((float)gd.TexCoords.Right / tex.Width, (float)gd.TexCoords.Bottom / tex.Height);
quadVertices[2].Color = color;
quadVertices[3].Position = new Vector3(currentPos + gd.DrawOffset + (gd.TexCoords.Width + topItalicOffset, 0.0f), 0.0f);
quadVertices[3].TextureCoordinate = ((float)gd.TexCoords.Right / tex.Width, (float)gd.TexCoords.Top / tex.Height);
quadVertices[3].Color = color;
sb.Draw(tex, quadVertices, 0.0f);
}
currentPos.X += gd.Advance;
}
}
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData, int rtdOffset = 0)
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, in ImmutableArray<RichTextData>? richTextData, int rtdOffset = 0, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
{
DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, richTextData, rtdOffset);
DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, richTextData, rtdOffset, alignment, forceUpperCase);
}
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData, int rtdOffset = 0)
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, in ImmutableArray<RichTextData>? richTextData, int rtdOffset = 0, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
if (!richTextData.HasValue || richTextData.Value.Length <= 0) { DrawString(sb, text, position, color, rotation, origin, scale, se, layerDepth, forceUpperCase: forceUpperCase); return; }
text = ApplyUpperCase(text, forceUpperCase);
float lineWidth = -1.0f;
Vector2 currentLineOffset = Vector2.Zero;
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
@@ -503,27 +583,21 @@ namespace Barotrauma
Vector2 advanceUnit = rotation == 0.0f ? Vector2.UnitX : new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
int richTextDataIndex = 0;
RichTextData currentRichTextData = richTextData[richTextDataIndex];
RichTextData currentRichTextData = richTextData.Value[richTextDataIndex];
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
{
lineNum++;
currentPos = position;
currentPos.X -= LineHeight * lineNum * advanceUnit.Y * scale.Y;
currentPos.Y += LineHeight * lineNum * advanceUnit.X * scale.Y;
continue;
}
uint charIndex = text[i];
HandleNewLineAndAlignment(text, advanceUnit, position, scale, alignment, i,
ref lineWidth, ref currentLineOffset, ref lineNum, ref currentPos,
out uint charIndex, out bool shouldContinue);
if (shouldContinue) { continue; }
Color currentTextColor;
while (currentRichTextData != null && i + rtdOffset > currentRichTextData.EndIndex + lineNum)
{
richTextDataIndex++;
currentRichTextData = richTextDataIndex < richTextData.Count ? richTextData[richTextDataIndex] : null;
currentRichTextData = richTextDataIndex < richTextData.Value.Length ? richTextData.Value[richTextDataIndex] : null;
}
if (currentRichTextData != null && currentRichTextData.StartIndex + lineNum <= i + rtdOffset && i + rtdOffset <= currentRichTextData.EndIndex + lineNum)
@@ -547,7 +621,7 @@ namespace Barotrauma
drawOffset.X = gd.DrawOffset.X * advanceUnit.X * scale.X - gd.DrawOffset.Y * advanceUnit.Y * scale.Y;
drawOffset.Y = gd.DrawOffset.X * advanceUnit.Y * scale.Y + gd.DrawOffset.Y * advanceUnit.X * scale.X;
sb.Draw(tex, currentPos + drawOffset, gd.TexCoords, currentTextColor, rotation, origin, scale, se, layerDepth);
sb.Draw(tex, currentPos + currentLineOffset + drawOffset, gd.TexCoords, currentTextColor, rotation, origin, scale, se, layerDepth);
}
currentPos += gd.Advance * advanceUnit * scale.X;
}
@@ -628,6 +702,8 @@ namespace Barotrauma
//A breaker (whitespace or CJK) was found earlier
//in this line, so let's break the line there
i = lastBreakerIndex.Value + 1;
gd = GetGlyphData(text[i]);
advance = gd.Advance;
}
nextLine();
@@ -648,7 +724,12 @@ namespace Barotrauma
requestedCharPos = foundCharPos;
return result;
}
public Vector2 MeasureString(LocalizedString str, bool removeExtraSpacing = false)
{
return MeasureString(str.Value, removeExtraSpacing);
}
public Vector2 MeasureString(string text, bool removeExtraSpacing = false)
{
if (text == null)

View File

@@ -25,12 +25,14 @@ namespace Barotrauma
get { return _toggleOpen; }
set
{
_toggleOpen = GameMain.Config.ChatOpen = value;
if (value) hideableElements.Visible = true;
_toggleOpen = PreferChatBoxOpen = value;
if (value) { hideableElements.Visible = true; }
}
}
private float openState;
public static bool PreferChatBoxOpen = true;
public bool CloseAfterMessageSent;
private float prevUIScale;
@@ -99,6 +101,7 @@ namespace Barotrauma
var channelSettingsContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), channelSettingsFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
CanBeFocused = true,
RelativeSpacing = 0.01f
};
@@ -119,9 +122,9 @@ namespace Barotrauma
Color = new Color(51, 59, 46),
SpriteEffects = Microsoft.Xna.Framework.Graphics.SpriteEffects.FlipHorizontally
};
arrowIcon.HoverColor = arrowIcon.PressedColor = arrowIcon.PressedColor = arrowIcon.Color;
arrowIcon.HoverColor = arrowIcon.PressedColor = arrowIcon.SelectedColor = arrowIcon.Color;
channelText = new GUITextBox(new RectTransform(new Vector2(0.25f, 0.8f), channelSettingsContent.RectTransform), style: "DigitalFrameLight", textAlignment: Alignment.Center, font: GUI.DigitalFont)
channelText = new GUITextBox(new RectTransform(new Vector2(0.25f, 0.8f), channelSettingsContent.RectTransform), style: "DigitalFrameLight", textAlignment: Alignment.Center, font: GUIStyle.DigitalFont)
{
textFilterFunction = text =>
{
@@ -173,7 +176,7 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), channelPickerContent.RectTransform), i.ToString(), style: "GUITextBlock")
{
TextColor = new Color(51, 59, 46),
SelectedTextColor = GUI.Style.Green,
SelectedTextColor = GUIStyle.Green,
UserData = i,
OnClicked = (btn, userdata) =>
{
@@ -185,13 +188,13 @@ namespace Barotrauma
int.TryParse(channelText.Text, out int newChannel);
radio.SetChannelMemory(index, newChannel);
btn.ToolTip = TextManager.GetWithVariables("radiochannelpreset",
new string[] { "[index]", "[channel]" },
new string[] { index.ToString(), radio.GetChannelMemory(index).ToString() });
("[index]", index.ToString()),
("[channel]", radio.GetChannelMemory(index).ToString()));
channelMemPending = false;
channelPickerContent.Children.First().CanBeFocused = true;
memButton.Enabled = true;
channelPickerContent.Flash(GUI.Style.Green);
channelText.Flash(GUI.Style.Green);
channelPickerContent.Flash(GUIStyle.Green);
channelText.Flash(GUIStyle.Green);
}
SetChannel(radio.GetChannelMemory(index), setText: true);
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
@@ -224,7 +227,7 @@ namespace Barotrauma
style: "ChatTextBox")
{
OverflowClip = true,
Font = GUI.SmallFont,
Font = GUIStyle.SmallFont,
MaxTextLength = ChatMessage.MaxLength
};
@@ -265,7 +268,7 @@ namespace Barotrauma
};
showNewMessagesButton.Visible = false;
ToggleOpen = GameMain.Config.ChatOpen;
ToggleOpen = PreferChatBoxOpen = GameSettings.CurrentConfig.ChatOpen;
}
public bool TypingChatMessage(GUITextBox textBox, string text)
@@ -337,7 +340,7 @@ namespace Barotrauma
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f);
GUITextBlock senderNameTimestamp = new GUITextBlock(new RectTransform(new Vector2(0.98f, 0.0f), msgHolder.RectTransform) { AbsoluteOffset = new Point((int)(5 * GUI.Scale), 0) },
ChatMessage.GetTimeStamp(), textColor: Color.LightGray, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null)
ChatMessage.GetTimeStamp(), textColor: Color.LightGray, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft, style: null)
{
CanBeFocused = true
};
@@ -350,9 +353,9 @@ namespace Barotrauma
{
Padding = Vector4.Zero
},
Font = GUI.SmallFont,
Font = GUIStyle.SmallFont,
CanBeFocused = true,
ForceUpperCase = false,
ForceUpperCase = ForceUpperCase.No,
UserData = message.SenderClient,
OnClicked = (_, o) =>
{
@@ -379,8 +382,8 @@ namespace Barotrauma
var msgText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgHolder.RectTransform)
{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), senderNameTimestamp == null ? 0 : senderNameTimestamp.Rect.Height) },
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null, wrap: true,
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, parseRichText: true)
RichString.Rich(displayedText), textColor: message.Color, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft, style: null, wrap: true,
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f)
{
UserData = message.SenderName,
CanBeFocused = false
@@ -454,7 +457,7 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(senderName))
{
var senderText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
senderName, textColor: senderColor, style: null, font: GUI.SmallFont)
senderName, textColor: senderColor, style: null, font: GUIStyle.SmallFont)
{
CanBeFocused = false
};
@@ -462,7 +465,7 @@ namespace Barotrauma
senderText.RectTransform.MinSize = new Point(0, senderText.Rect.Height);
}
var msgPopupText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.BottomLeft, style: null, wrap: true, parseRichText: true)
RichString.Rich(displayedText), textColor: message.Color, font: GUIStyle.SmallFont, textAlignment: Alignment.BottomLeft, style: null, wrap: true)
{
CanBeFocused = false
};
@@ -553,8 +556,8 @@ namespace Barotrauma
{
int index = (int)presetButton.UserData;
presetButton.ToolTip = TextManager.GetWithVariables("radiochannelpreset",
new string[] { "[index]", "[channel]" },
new string[] { index.ToString(), radio.GetChannelMemory(index).ToString() });
("[index]", index.ToString()),
("[channel]", radio.GetChannelMemory(index).ToString()));
}
SetChannel(radio.Channel, setText: true);
prevRadio = radio;
@@ -563,7 +566,7 @@ namespace Barotrauma
{
if (channelPickerContent.FlashTimer <= 0)
{
channelPickerContent.Flash(GUI.Style.Green, flashRectInflate: new Vector2(GUI.Scale * 5.0f));
channelPickerContent.Flash(GUIStyle.Green, flashRectInflate: new Vector2(GUI.Scale * 5.0f));
}
if (PlayerInput.PrimaryMouseButtonClicked() && !GUI.IsMouseOn(channelPickerContent))
{
@@ -671,7 +674,7 @@ namespace Barotrauma
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
radio.Channel = channel;
GameMain.Client?.CreateEntityEvent(radio.Item, new object[] { NetEntityEvent.Type.ChangeProperty, radio.SerializableProperties["channel"] });
GameMain.Client?.CreateEntityEvent(radio.Item, new Item.ChangePropertyEventData(radio.SerializableProperties["channel".ToIdentifier()]));
if (setText)
{

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -16,7 +17,7 @@ namespace Barotrauma
Toggle
}
public class GUIComponentStyle
public class GUIComponentStyle : GUIPrefab
{
public readonly Vector4 Padding;
@@ -35,31 +36,53 @@ namespace Barotrauma
public readonly float ColorCrossFadeTime;
public readonly TransitionMode TransitionMode;
public readonly string Font;
public readonly Identifier Font;
public readonly bool ForceUpperCase;
public readonly Color OutlineColor;
public readonly XElement Element;
public readonly ContentXElement Element;
public readonly Dictionary<GUIComponent.ComponentState, List<UISprite>> Sprites;
public SpriteFallBackState FallBackState;
public Dictionary<string, GUIComponentStyle> ChildStyles;
public readonly GUIStyle Style;
public readonly GUIComponentStyle ParentStyle;
public readonly Dictionary<Identifier, GUIComponentStyle> ChildStyles;
public static GUIComponentStyle FromHierarchy(IReadOnlyList<Identifier> hierarchy)
{
if (hierarchy is null || hierarchy.None()) { return null; }
GUIStyle.ComponentStyles.TryGet(hierarchy[0], out GUIComponentStyle style);
for (int i = 1; i < hierarchy.Count; i++)
{
if (style is null) { return null; }
style.ChildStyles.TryGetValue(hierarchy[i], out style);
}
return style;
}
public static Identifier[] ToHierarchy(GUIComponentStyle style)
{
List<Identifier> ids = new List<Identifier>();
while (style != null)
{
ids.Insert(0, style.Identifier);
style = style.ParentStyle;
}
return ids.ToArray();
}
public readonly string Name;
public int? Width { get; private set; }
public int? Height { get; private set; }
public GUIComponentStyle(XElement element, GUIStyle style)
public GUIComponentStyle(ContentXElement element, UIStyleFile file, GUIComponentStyle parent = null) : base(element, file)
{
Name = element.Name.LocalName;
Style = style;
Element = element;
Sprites = new Dictionary<GUIComponent.ComponentState, List<UISprite>>();
@@ -68,7 +91,8 @@ namespace Barotrauma
Sprites[state] = new List<UISprite>();
}
ChildStyles = new Dictionary<string, GUIComponentStyle>();
ParentStyle = parent;
ChildStyles = new Dictionary<Identifier, GUIComponentStyle>();
Padding = element.GetAttributeVector4("padding", Vector4.Zero);
@@ -95,10 +119,10 @@ namespace Barotrauma
FallBackState = s;
}
Font = element.GetAttributeString("font", "");
Font = element.GetAttributeIdentifier("font", "");
ForceUpperCase = element.GetAttributeBool("forceuppercase", false);
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -106,7 +130,7 @@ namespace Barotrauma
UISprite newSprite = new UISprite(subElement);
GUIComponent.ComponentState spriteState = GUIComponent.ComponentState.None;
if (subElement.Attribute("state") != null)
if (subElement.GetAttribute("state") != null)
{
string stateStr = subElement.GetAttributeString("state", "None");
Enum.TryParse(stateStr, out spriteState);
@@ -128,15 +152,15 @@ namespace Barotrauma
case "size":
break;
default:
string styleName = subElement.Name.ToString().ToLowerInvariant();
Identifier styleName = subElement.NameAsIdentifier();
if (ChildStyles.ContainsKey(styleName))
{
DebugConsole.ThrowError("UI style \"" + element.Name.ToString() + "\" contains multiple child styles with the same name (\"" + styleName + "\")!");
ChildStyles[styleName] = new GUIComponentStyle(subElement, style);
ChildStyles[styleName] = new GUIComponentStyle(subElement, file, this);
}
else
{
ChildStyles.Add(styleName, new GUIComponentStyle(subElement, style));
ChildStyles.Add(styleName, new GUIComponentStyle(subElement, file, this));
}
break;
}
@@ -157,7 +181,7 @@ namespace Barotrauma
public void GetSize(XElement element)
{
Point size = new Point(0, 0);
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("size", StringComparison.OrdinalIgnoreCase)) { continue; }
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
@@ -172,5 +196,7 @@ namespace Barotrauma
if (size.X > 0) { Width = size.X; }
if (size.Y > 0) { Height = size.Y; }
}
public override void Dispose() { }
}
}

View File

@@ -22,7 +22,7 @@ namespace Barotrauma
private GUIButton clearAllButton;
private List<CharacterInfo> PendingHires => campaign.Map?.CurrentLocation?.HireManager?.PendingHires;
private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign();
private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign(ClientPermissions.ManageHires);
private Point resolutionWhenCreated;
@@ -108,10 +108,10 @@ namespace Barotrauma
};
var imageWidth = (float)headerGroup.Rect.Height / headerGroup.Rect.Width;
new GUIImage(new RectTransform(new Vector2(imageWidth, 1.0f), headerGroup.RectTransform), "CrewManagementHeaderIcon");
new GUITextBlock(new RectTransform(new Vector2(1.0f - imageWidth, 1.0f), headerGroup.RectTransform), TextManager.Get("campaigncrew.header"), font: GUI.LargeFont)
new GUITextBlock(new RectTransform(new Vector2(1.0f - imageWidth, 1.0f), headerGroup.RectTransform), TextManager.Get("campaigncrew.header"), font: GUIStyle.LargeFont)
{
CanBeFocused = false,
ForceUpperCase = true
ForceUpperCase = ForceUpperCase.Yes
};
var hireablesGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
@@ -162,17 +162,17 @@ namespace Barotrauma
RelativeSpacing = 0.005f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform),
TextManager.Get("campaignstore.balance"), font: GUI.Font, textAlignment: Alignment.BottomRight)
TextManager.Get("campaignstore.balance"), font: GUIStyle.Font, textAlignment: Alignment.BottomRight)
{
AutoScaleVertical = true,
ForceUpperCase = true
ForceUpperCase = ForceUpperCase.Yes
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform),
"", font: GUI.SubHeadingFont, textAlignment: Alignment.TopRight)
"", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.TopRight)
{
AutoScaleVertical = true,
TextScale = 1.1f,
TextGetter = () => FormatCurrency(campaign.Money)
TextGetter = () => TextManager.FormatCurrency(campaign.Wallet.Balance)
};
var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
@@ -182,13 +182,13 @@ namespace Barotrauma
}).RectTransform));
float height = 0.05f;
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaigncrew.pending"), font: GUI.SubHeadingFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaigncrew.pending"), font: GUIStyle.SubHeadingFont);
pendingList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform))
{
Spacing = 1
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUI.SubHeadingFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUIStyle.SubHeadingFont);
crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform))
{
Spacing = 1
@@ -196,7 +196,7 @@ namespace Barotrauma
var group = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), group.RectTransform), TextManager.Get("campaignstore.total"));
totalBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), group.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
totalBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), group.RectTransform), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right)
{
TextScale = 1.1f
};
@@ -207,12 +207,12 @@ namespace Barotrauma
validateHiresButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
{
ClickSound = GUISoundType.HireRepairClick,
ForceUpperCase = true,
ForceUpperCase = ForceUpperCase.Yes,
OnClicked = (b, o) => ValidateHires(PendingHires, true)
};
clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
{
ForceUpperCase = true,
ForceUpperCase = ForceUpperCase.Yes,
Enabled = HasPermission,
OnClicked = (b, o) => RemoveAllPendingHires()
};
@@ -302,30 +302,42 @@ namespace Barotrauma
if (sortingMethod == SortingMethod.AlphabeticalAsc)
{
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Name.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Name));
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Name.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Name));
}
else if (sortingMethod == SortingMethod.JobAsc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
String.Compare((x.GUIComponent.UserData as Tuple<CharacterInfo, float>)?.Item1.Job.Name, (y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Job.Name, StringComparison.Ordinal));
String.Compare(((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Job.Name.Value, ((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Job.Name.Value, StringComparison.Ordinal));
}
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Salary.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Salary));
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Salary.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Salary));
if (sortingMethod == SortingMethod.PriceDesc) { list.Content.RectTransform.ReverseChildren(); }
}
else if (sortingMethod == SortingMethod.SkillAsc || sortingMethod == SortingMethod.SkillDesc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item2.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item2));
((InfoSkill)x.GUIComponent.UserData).SkillLevel.CompareTo(((InfoSkill)y.GUIComponent.UserData).SkillLevel));
if (sortingMethod == SortingMethod.SkillDesc) { list.Content.RectTransform.ReverseChildren(); }
}
}
private readonly struct InfoSkill
{
public readonly CharacterInfo CharacterInfo;
public readonly float SkillLevel;
public InfoSkill(CharacterInfo characterInfo, float skillLevel)
{
CharacterInfo = characterInfo;
SkillLevel = skillLevel;
}
}
private void CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox)
{
Skill skill = null;
@@ -338,7 +350,7 @@ namespace Barotrauma
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 55)), parent: listBox.Content.RectTransform), "ListBoxElement")
{
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill?.Level ?? 0.0f)
UserData = new InfoSkill(characterInfo, skill?.Level ?? 0.0f)
};
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
@@ -363,7 +375,7 @@ namespace Barotrauma
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
characterInfo.Job.Name, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft)
characterInfo.Job.Name, textColor: Color.White, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft)
{
CanBeFocused = false
};
@@ -374,7 +386,7 @@ namespace Barotrauma
{
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true);
float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width;
GUIImage skillIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 1.0f), skillGroup.RectTransform), skill.Icon)
GUIImage skillIcon = new GUIImage(new RectTransform(Vector2.One, skillGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), skill.Icon, scaleToFit: true)
{
CanBeFocused = false
};
@@ -388,7 +400,7 @@ namespace Barotrauma
if (listBox != crewList)
{
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
FormatCurrency(characterInfo.Salary),
TextManager.FormatCurrency(characterInfo.Salary),
textAlignment: Alignment.Center)
{
CanBeFocused = false
@@ -421,7 +433,7 @@ namespace Barotrauma
else if (!btn.Enabled)
{
btn.ToolTip = string.Empty;
btn.Enabled = true;
btn.Enabled = HasPermission;
}
};
@@ -448,7 +460,7 @@ namespace Barotrauma
var confirmDialog = new GUIMessageBox(
TextManager.Get("FireWarningHeader"),
TextManager.GetWithVariable("FireWarningText", "[charactername]", ((CharacterInfo)obj).Name),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
confirmDialog.Buttons[0].UserData = (CharacterInfo)obj;
confirmDialog.Buttons[0].OnClicked = FireCharacter;
confirmDialog.Buttons[0].OnClicked += confirmDialog.Close;
@@ -510,10 +522,11 @@ namespace Barotrauma
string name = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
nameBlock.Text = ToolBox.LimitString(name, nameBlock.Font, nameBlock.Rect.Width);
if (characterInfo.HasGenders)
if (characterInfo.HasSpecifierTags)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("gender"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get(characterInfo.Gender.ToString()));
var menuCategoryVar = characterInfo.Prefab.MenuCategoryVar;
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(menuCategoryVar));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get(characterInfo.ReplaceVars($"[{menuCategoryVar}]")));
}
if (characterInfo.Job is Job job)
{
@@ -523,7 +536,7 @@ namespace Barotrauma
if (characterInfo.PersonalityTrait is NPCPersonalityTrait trait)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get("personalitytrait." + trait.Name.Replace(" ", "")));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get("personalitytrait." + trait.Name.Replace(" ".ToIdentifier(), Identifier.Empty)));
}
infoLabelGroup.Recalculate();
infoValueGroup.Recalculate();
@@ -568,7 +581,7 @@ namespace Barotrauma
return false;
}
hireableList.Content.RemoveChild(hireableList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
hireableList.Content.RemoveChild(hireableList.Content.FindChild(c => ((InfoSkill)c.UserData).CharacterInfo == characterInfo));
hireableList.UpdateScrollBarSize();
if (!PendingHires.Contains(characterInfo)) { PendingHires.Add(characterInfo); }
CreateCharacterFrame(characterInfo, pendingList);
@@ -582,14 +595,14 @@ namespace Barotrauma
private bool RemovePendingHire(CharacterInfo characterInfo, bool setTotalHireCost = true, bool createNetworkMessage = true)
{
if (PendingHires.Contains(characterInfo)) { PendingHires.Remove(characterInfo); }
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => ((InfoSkill)c.UserData).CharacterInfo == characterInfo));
pendingList.UpdateScrollBarSize();
// Server will reset the names to originals in multiplayer
if (!GameMain.IsMultiplayer) { characterInfo?.ResetName(); }
if (campaign.Map.CurrentLocation.HireManager.AvailableCharacters.Any(info => info.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()) &&
hireableList.Content.Children.None(c => c.UserData is Tuple<CharacterInfo, float> userData && userData.Item1.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()))
hireableList.Content.Children.None(c => c.UserData is InfoSkill userData && userData.CharacterInfo.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()))
{
CreateCharacterFrame(characterInfo, hireableList);
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
@@ -603,7 +616,7 @@ namespace Barotrauma
private bool RemoveAllPendingHires(bool createNetworkMessage = true)
{
pendingList.Content.Children.ToList().ForEach(c => RemovePendingHire((c.UserData as Tuple<CharacterInfo, float>).Item1, setTotalHireCost: false, createNetworkMessage));
pendingList.Content.Children.ToList().ForEach(c => RemovePendingHire(((InfoSkill)c.UserData).CharacterInfo, setTotalHireCost: false, createNetworkMessage));
SetTotalHireCost();
return true;
}
@@ -614,12 +627,12 @@ namespace Barotrauma
int total = 0;
pendingList.Content.Children.ForEach(c =>
{
total += (c.UserData as Tuple<CharacterInfo, float>).Item1.Salary;
total += ((InfoSkill)c.UserData).CharacterInfo.Salary;
});
totalBlock.Text = FormatCurrency(total);
bool enoughMoney = campaign != null ? total <= campaign.Money : true;
totalBlock.Text = TextManager.FormatCurrency(total);
bool enoughMoney = campaign == null || campaign.Wallet.CanAfford(total);
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any();
validateHiresButton.Enabled = enoughMoney && HasPermission && pendingList.Content.RectTransform.Children.Any();
}
public bool ValidateHires(List<CharacterInfo> hires, bool createNetworkEvent = false)
@@ -639,7 +652,7 @@ namespace Barotrauma
int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary);
if (total > campaign.Money) { return false; }
if (!campaign.Wallet.CanAfford(total)) { return false; }
bool atLeastOneHired = false;
foreach (CharacterInfo ci in nonDuplicateHires)
@@ -661,7 +674,7 @@ namespace Barotrauma
var dialog = new GUIMessageBox(
TextManager.Get("newcrewmembers"),
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.Name),
new string[] { TextManager.Get("Ok") });
new LocalizedString[] { TextManager.Get("Ok") });
dialog.Buttons[0].OnClicked += dialog.Close;
}
@@ -687,7 +700,7 @@ namespace Barotrauma
RelativeSpacing = 0.02f,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), layoutGroup.RectTransform), TextManager.Get("campaigncrew.givenickname"), font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), layoutGroup.RectTransform), TextManager.Get("campaigncrew.givenickname"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
var groupElementSize = new Vector2(1.0f, 0.25f);
var nameBox = new GUITextBox(new RectTransform(groupElementSize, layoutGroup.RectTransform))
{
@@ -732,7 +745,7 @@ namespace Barotrauma
}
else
{
var crewComponent = crewList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo);
var crewComponent = crewList.Content.FindChild(c => ((InfoSkill)c.UserData).CharacterInfo == characterInfo);
if (crewComponent != null)
{
crewList.Content.RemoveChild(crewComponent);
@@ -742,7 +755,7 @@ namespace Barotrauma
}
else
{
var pendingComponent = pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo);
var pendingComponent = pendingList.Content.FindChild(c => ((InfoSkill)c.UserData).CharacterInfo == characterInfo);
if (pendingComponent != null)
{
pendingList.Content.RemoveChild(pendingComponent);
@@ -821,15 +834,15 @@ namespace Barotrauma
characterPreviewFrame = null;
}
static (GUIComponent, CharacterInfo) FindHighlightedCharacter(GUIComponent c)
static (GUIComponent GuiComponent, CharacterInfo CharacterInfo) FindHighlightedCharacter(GUIComponent c)
{
if (c == null)
{
return default;
}
if (c.UserData is Tuple<CharacterInfo, float> highlightedData)
if (c.UserData is InfoSkill highlightedData)
{
return (c, highlightedData.Item1);
return (c, highlightedData.CharacterInfo);
}
if (c.Parent != null)
{
@@ -912,7 +925,5 @@ namespace Barotrauma
GameMain.Client.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
}
private string FormatCurrency(int currency) => TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", currency));
}
}

View File

@@ -1,9 +1,11 @@
using Microsoft.Xna.Framework;
#nullable enable
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Text;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -18,7 +20,7 @@ namespace Barotrauma
}
set
{
if (value && backgroundFrame == null) { Init(); }
if (value) { InitIfNecessary(); }
if (!value)
{
fileSystemWatcher?.Dispose();
@@ -28,26 +30,31 @@ namespace Barotrauma
}
}
private static GUIFrame backgroundFrame;
private static GUIFrame window;
private static GUIListBox sidebar;
private static GUIListBox fileList;
private static GUITextBox directoryBox;
private static GUITextBox filterBox;
private static GUITextBox fileBox;
private static GUIDropDown fileTypeDropdown;
private static GUIButton openButton;
private static GUIFrame? backgroundFrame;
private static GUIFrame? window;
private static GUIListBox? sidebar;
private static GUIListBox? fileList;
private static GUITextBox? directoryBox;
private static GUITextBox? filterBox;
private static GUITextBox? fileBox;
private static GUIDropDown? fileTypeDropdown;
private static GUIButton? openButton;
private static System.IO.FileSystemWatcher fileSystemWatcher;
private static System.IO.FileSystemWatcher? fileSystemWatcher;
private static string currentFileTypePattern;
private enum ItemIsDirectory
{
Yes, No
}
private static readonly string[] ignoredDrivePrefixes = new string[]
private static string? currentFileTypePattern;
private static readonly string[] ignoredDrivePrefixes =
{
"/sys/", "/snap/"
};
private static string currentDirectory;
private static string currentDirectory = "";
public static string CurrentDirectory
{
get
@@ -91,7 +98,7 @@ namespace Barotrauma
}
}
public static Action<string> OnFileSelected
public static Action<string>? OnFileSelected
{
get;
set;
@@ -99,15 +106,16 @@ namespace Barotrauma
private static void OnFileSystemChanges(object sender, System.IO.FileSystemEventArgs e)
{
if (fileList is null) { return; }
switch (e.ChangeType)
{
case System.IO.WatcherChangeTypes.Created:
{
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), e.Name)
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), e.Name ?? string.Empty)
{
UserData = (bool?)Directory.Exists(e.FullPath)
UserData = Directory.Exists(e.FullPath) ? ItemIsDirectory.Yes : ItemIsDirectory.No
};
if ((itemFrame.UserData as bool?) ?? false)
if (itemFrame.UserData is ItemIsDirectory.Yes)
{
itemFrame.Text += "/";
}
@@ -122,11 +130,13 @@ namespace Barotrauma
break;
case System.IO.WatcherChangeTypes.Renamed:
{
System.IO.RenamedEventArgs renameArgs = e as System.IO.RenamedEventArgs;
var itemFrame = fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == renameArgs.OldName || tb.Text == renameArgs.OldName + "/")) as GUITextBlock;
itemFrame.UserData = (bool?)Directory.Exists(e.FullPath);
itemFrame.Text = renameArgs.Name;
if ((itemFrame.UserData as bool?) ?? false)
System.IO.RenamedEventArgs renameArgs = e as System.IO.RenamedEventArgs ?? throw new InvalidCastException($"Unable to cast {nameof(System.IO.FileSystemEventArgs)} to {nameof(System.IO.RenamedEventArgs)}.");
var itemFrame =
fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == renameArgs.OldName || tb.Text == renameArgs.OldName + "/")) as GUITextBlock
?? throw new Exception($"Could not find file list item with name \"{renameArgs.OldName}\"");
itemFrame.UserData = Directory.Exists(e.FullPath) ? ItemIsDirectory.Yes : ItemIsDirectory.No;
itemFrame.Text = renameArgs.Name ?? string.Empty;
if (itemFrame.UserData is ItemIsDirectory.Yes)
{
itemFrame.Text += "/";
}
@@ -138,10 +148,10 @@ namespace Barotrauma
private static int SortFiles(RectTransform r1, RectTransform r2)
{
string file1 = (r1.GUIComponent as GUITextBlock)?.Text ?? "";
string file2 = (r2.GUIComponent as GUITextBlock)?.Text ?? "";
bool dir1 = (r1.GUIComponent.UserData as bool?) ?? false;
bool dir2 = (r2.GUIComponent.UserData as bool?) ?? false;
string file1 = (r1.GUIComponent as GUITextBlock)?.Text?.SanitizedValue ?? "";
string file2 = (r2.GUIComponent as GUITextBlock)?.Text?.SanitizedValue ?? "";
bool dir1 = r1.GUIComponent.UserData is ItemIsDirectory.Yes;
bool dir2 = r2.GUIComponent.UserData is ItemIsDirectory.Yes;
if (dir1 && !dir2)
{
return -1;
@@ -154,6 +164,11 @@ namespace Barotrauma
return string.Compare(file1, file2);
}
private static void InitIfNecessary()
{
if (backgroundFrame == null) { Init(); }
}
public static void Init()
{
backgroundFrame = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas), style: null)
@@ -179,7 +194,7 @@ namespace Barotrauma
sidebar.OnSelected = (child, userdata) =>
{
CurrentDirectory = (child as GUITextBlock).Text;
CurrentDirectory = (child as GUITextBlock)?.Text.SanitizedValue ?? throw new Exception("Sidebar selection is invalid");
return false;
};
@@ -228,13 +243,14 @@ namespace Barotrauma
{
OnSelected = (child, userdata) =>
{
if (userdata == null) { return false; }
if (userdata is null) { return false; }
if (fileBox is null) { return false; }
var fileName = (child as GUITextBlock).Text;
var fileName = (child as GUITextBlock)!.Text.SanitizedValue;
fileBox.Text = fileName;
if (PlayerInput.DoubleClicked())
{
bool isDir = (userdata as bool?).Value;
bool isDir = userdata is ItemIsDirectory.Yes;
if (isDir)
{
CurrentDirectory += fileName;
@@ -263,7 +279,7 @@ namespace Barotrauma
{
OnSelected = (child, userdata) =>
{
currentFileTypePattern = (child as GUITextBlock).UserData as string;
currentFileTypePattern = (child as GUITextBlock)!.UserData as string;
RefreshFileList();
return true;
@@ -307,30 +323,31 @@ namespace Barotrauma
public static void ClearFileTypeFilters()
{
if (backgroundFrame == null) { Init(); }
fileTypeDropdown.ClearChildren();
InitIfNecessary();
fileTypeDropdown!.ClearChildren();
}
public static void AddFileTypeFilter(string name, string pattern)
{
if (backgroundFrame == null) { Init(); }
fileTypeDropdown.AddItem(name + " (" + pattern + ")", pattern);
InitIfNecessary();
fileTypeDropdown!.AddItem(name + " (" + pattern + ")", pattern);
}
public static void SelectFileTypeFilter(string pattern)
{
if (backgroundFrame == null) { Init(); }
fileTypeDropdown.SelectItem(pattern);
InitIfNecessary();
fileTypeDropdown!.SelectItem(pattern);
}
public static void RefreshFileList()
{
fileList.Content.ClearChildren();
InitIfNecessary();
fileList!.Content.ClearChildren();
fileList.BarScroll = 0.0f;
try
{
var directories = Directory.EnumerateDirectories(currentDirectory, "*" + filterBox.Text + "*");
var directories = Directory.EnumerateDirectories(currentDirectory, "*" + filterBox!.Text + "*");
foreach (var directory in directories)
{
string txt = directory;
@@ -338,7 +355,7 @@ namespace Barotrauma
if (!txt.EndsWith("/")) { txt += "/"; }
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
{
UserData = (bool?)true
UserData = ItemIsDirectory.Yes
};
var folderIcon = new GUIImage(new RectTransform(new Point((int)(itemFrame.Rect.Height * 0.8f)), itemFrame.RectTransform, Anchor.CenterLeft)
{
@@ -347,18 +364,18 @@ namespace Barotrauma
itemFrame.Padding = new Vector4(folderIcon.Rect.Width * 1.5f, itemFrame.Padding.Y, itemFrame.Padding.Z, itemFrame.Padding.W);
}
IEnumerable<string> files = null;
if (currentFileTypePattern == null)
IEnumerable<string> files = Enumerable.Empty<string>();
if (currentFileTypePattern.IsNullOrEmpty())
{
files = Directory.GetFiles(currentDirectory);
}
else
{
foreach (string pattern in currentFileTypePattern.Split(','))
foreach (string pattern in currentFileTypePattern!.Split(','))
{
string patternTrimmed = pattern.Trim();
patternTrimmed = "*" + filterBox.Text + "*" + patternTrimmed;
if (files == null)
if (files.None())
{
files = Directory.EnumerateFiles(currentDirectory, patternTrimmed);
}
@@ -375,7 +392,7 @@ namespace Barotrauma
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
{
UserData = (bool?)false
UserData = ItemIsDirectory.No
};
}
}
@@ -387,8 +404,8 @@ namespace Barotrauma
};
}
directoryBox.Text = currentDirectory;
fileBox.Text = "";
directoryBox!.Text = currentDirectory;
fileBox!.Text = "";
fileList.Deselect();
}
@@ -398,9 +415,9 @@ namespace Barotrauma
if (dir.EndsWith("/")) { dir = dir.Substring(0, dir.Length - 1); }
int index = dir.LastIndexOf("/");
if (index < 0) { return false; }
CurrentDirectory = CurrentDirectory.Substring(0, index+1);
CurrentDirectory = CurrentDirectory.Substring(0, index + 1);
return false;
return true;
}
public static void AddToGUIUpdateList()

File diff suppressed because it is too large Load Diff

View File

@@ -116,32 +116,32 @@ namespace Barotrauma
get { return Frame.FlashTimer; }
}
public override ScalableFont Font
public override GUIFont Font
{
get
{
return (textBlock == null) ? GUI.Font : textBlock.Font;
return (textBlock == null) ? GUIStyle.Font : textBlock.Font;
}
set
{
base.Font = value;
if (textBlock != null) textBlock.Font = value;
if (textBlock != null) { textBlock.Font = value; }
}
}
public string Text
public LocalizedString Text
{
get { return textBlock.Text; }
set { textBlock.Text = value; }
}
public bool ForceUpperCase
public ForceUpperCase ForceUpperCase
{
get { return textBlock.ForceUpperCase; }
set { textBlock.ForceUpperCase = value; }
}
public override string ToolTip
public override RichString ToolTip
{
get
{
@@ -160,39 +160,36 @@ namespace Barotrauma
private bool flashed;
public GUISoundType ClickSound { get; set; } = GUISoundType.Click;
public GUIButton(RectTransform rectT, string text = "", Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : base(style, rectT)
public GUIButton(RectTransform rectT, Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : this(rectT, new RawLString(""), textAlignment, style, color) { }
public GUIButton(RectTransform rectT, LocalizedString text, Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : base(style, rectT)
{
CanBeFocused = true;
HoverCursor = CursorState.Hand;
frame = new GUIFrame(new RectTransform(Vector2.One, rectT), style) { CanBeFocused = false };
if (style != null) { GUI.Style.Apply(frame, style == "" ? "GUIButton" : style); }
if (style != null) { GUIStyle.Apply(frame, style == "" ? "GUIButton" : style); }
if (color.HasValue)
{
this.color = frame.Color = color.Value;
}
var selfStyle = Style;
textBlock = new GUITextBlock(new RectTransform(Vector2.One, rectT, Anchor.Center), text, textAlignment: textAlignment, style: null)
{
TextColor = this.style == null ? Color.Black : this.style.TextColor,
HoverTextColor = this.style == null ? Color.Black : this.style.HoverTextColor,
SelectedTextColor = this.style == null ? Color.Black : this.style.SelectedTextColor,
TextColor = selfStyle?.TextColor ?? Color.Black,
HoverTextColor = selfStyle?.HoverTextColor ?? Color.Black,
SelectedTextColor = selfStyle?.SelectedTextColor ?? Color.Black,
CanBeFocused = false
};
if (rectT.Rect.Height == 0 && !string.IsNullOrEmpty(text))
if (rectT.Rect.Height == 0 && !text.IsNullOrEmpty())
{
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(textBlock.Text).Y));
RectTransform.MinSize = textBlock.RectTransform.MinSize = new Point(0, System.Math.Max(rectT.MinSize.Y, Rect.Height));
TextBlock.SetTextPos();
}
GUI.Style.Apply(textBlock, "", this);
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
//use the default CJK font as a fallback
if (TextManager.IsCJK(textBlock.Text) && !textBlock.Font.IsCJK)
{
textBlock.Font = GUI.CJKFont;
}
GUIStyle.Apply(textBlock, "", this);
Enabled = true;
}
@@ -217,7 +214,7 @@ namespace Barotrauma
float expand = (pulseExpand * 20.0f) * GUI.Scale;
expandRect.Inflate(expand, expand);
GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
GUIStyle.EndRoundButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
}
}

View File

@@ -5,7 +5,7 @@ using Microsoft.Xna.Framework;
namespace Barotrauma
{
public class GUIColorPicker : GUIComponent
public class GUIColorPicker : GUIComponent, IDisposable
{
public delegate bool OnColorSelectedHandler(GUIColorPicker component, Color color);
public OnColorSelectedHandler? OnColorSelected;
@@ -34,11 +34,6 @@ namespace Barotrauma
public GUIColorPicker(RectTransform rectT, string? style = null) : base(style, rectT) { }
~GUIColorPicker()
{
DisposeTextures();
}
private void Init()
{
int tWidth = Rect.Width;
@@ -170,10 +165,12 @@ namespace Barotrauma
}
}
public void DisposeTextures()
public void Dispose()
{
mainTexture?.Dispose();
mainTexture = null;
hueTexture?.Dispose();
hueTexture = null;
}
public void RefreshHue()

View File

@@ -8,6 +8,7 @@ using System.Xml.Linq;
using Barotrauma.IO;
using RestSharp;
using System.Net;
using System.Collections.Immutable;
namespace Barotrauma
{
@@ -39,7 +40,7 @@ namespace Barotrauma
public IEnumerable<T> GetAllChildren<T>() where T : GUIComponent
{
return GetAllChildren().Where(c => c is T).Select(c => c as T);
return GetAllChildren().OfType<T>();
}
/// <summary>
@@ -66,7 +67,7 @@ namespace Barotrauma
{
foreach (GUIComponent child in Children)
{
if (child.UserData == obj || (child.userData != null && child.userData.Equals(obj))) { return child; }
if (Equals(child.UserData, obj)) { return child; }
}
return null;
}
@@ -107,7 +108,7 @@ namespace Barotrauma
}
public GUIComponent FindChild(object userData, bool recursive = false)
{
var matchingChild = Children.FirstOrDefault(c => c.userData == userData);
var matchingChild = Children.FirstOrDefault(c => Equals(c.UserData, userData));
if (recursive && matchingChild == null)
{
foreach (GUIComponent child in Children)
@@ -122,7 +123,7 @@ namespace Barotrauma
public IEnumerable<GUIComponent> FindChildren(object userData)
{
return Children.Where(c => c.userData == userData);
return Children.Where(c => Equals(c.UserData, userData));
}
public IEnumerable<GUIComponent> FindChildren(Func<GUIComponent, bool> predicate)
@@ -161,9 +162,7 @@ namespace Barotrauma
protected Alignment alignment;
protected GUIComponentStyle style;
protected object userData;
protected Identifier[] styleHierarchy;
public bool CanBeFocused;
@@ -206,16 +205,14 @@ namespace Barotrauma
}
}
public virtual ScalableFont Font
public virtual GUIFont Font
{
get;
set;
}
// Use the rawtooltip when copying displayed tooltips so that any possible color-data related values are translated over as well
public string RawToolTip;
private string toolTip;
public virtual string ToolTip
private RichString toolTip;
public virtual RichString ToolTip
{
get
{
@@ -223,18 +220,12 @@ namespace Barotrauma
}
set
{
RawToolTip = value;
TooltipRichTextData = RichTextData.GetRichTextData(value, out value);
toolTip = value;
}
}
public List<RichTextData> TooltipRichTextData = null;
public GUIComponentStyle Style
{
get { return style; }
}
=> GUIComponentStyle.FromHierarchy(styleHierarchy);
public bool Visible
{
@@ -258,8 +249,8 @@ namespace Barotrauma
protected Rectangle ClampRect(Rectangle r)
{
if (Parent == null || !ClampMouseRectToParent) { return r; }
Rectangle parentRect = Parent.ClampRect(Parent.Rect);
if (Parent is null) { return r; }
Rectangle parentRect = !Parent.ClampMouseRectToParent ? Parent.Rect : Parent.ClampRect(Parent.Rect);
if (parentRect.Width <= 0 || parentRect.Height <= 0) { return Rectangle.Empty; }
if (parentRect.X > r.X)
{
@@ -293,11 +284,13 @@ namespace Barotrauma
}
public bool ClampMouseRectToParent { get; set; } = false;
public virtual Rectangle MouseRect
{
get
{
if (!CanBeFocused) { return Rectangle.Empty; }
return ClampMouseRectToParent ? ClampRect(Rect) : Rect;
}
}
@@ -310,13 +303,13 @@ namespace Barotrauma
protected ComponentState _state;
protected ComponentState _previousState;
protected bool selected;
protected bool isSelected;
public virtual bool Selected
{
get { return selected; }
get { return isSelected; }
set
{
selected = value;
isSelected = value;
foreach (var child in Children)
{
child.Selected = value;
@@ -338,11 +331,8 @@ namespace Barotrauma
}
}
public object UserData
{
get { return userData; }
set { userData = value; }
}
#warning TODO: this is cursed, stop using this
public object UserData;
public int CountChildren
{
@@ -417,20 +407,20 @@ namespace Barotrauma
Visible = true;
OutlineColor = Color.Transparent;
Font = GUI.Font;
Font = GUIStyle.Font;
CanBeFocused = true;
if (style != null) { GUI.Style.Apply(this, style); }
if (style != null) { GUIStyle.Apply(this, style); }
}
protected GUIComponent(string style)
{
Visible = true;
OutlineColor = Color.Transparent;
Font = GUI.Font;
Font = GUIStyle.Font;
CanBeFocused = true;
if (style != null) { GUI.Style.Apply(this, style); }
if (style != null) { GUIStyle.Apply(this, style); }
}
#region Updating
@@ -486,7 +476,7 @@ namespace Barotrauma
{
if (GUI.IsMouseOn(this) && PlayerInput.SecondaryMouseButtonClicked())
{
OnSecondaryClicked?.Invoke(this, userData);
OnSecondaryClicked?.Invoke(this, UserData);
}
}
@@ -704,7 +694,7 @@ namespace Barotrauma
if (GlowOnSelect && State == ComponentState.Selected)
{
GUI.UIGlow.Draw(spriteBatch, Rect, SelectedColor);
GUIStyle.UIGlow.Draw(spriteBatch, Rect, SelectedColor);
}
if (flashTimer > 0.0f)
@@ -724,7 +714,7 @@ namespace Barotrauma
}
else
{
var glow = useCircularFlash ? GUI.UIGlowCircular : GUI.UIGlow;
var glow = useCircularFlash ? GUIStyle.UIGlowCircular : GUIStyle.UIGlow;
glow.Draw(spriteBatch,
flashRect,
flashColor * (float)Math.Sin(flashTimer % flashCycleDuration / flashCycleDuration * MathHelper.Pi * 0.8f));
@@ -738,24 +728,24 @@ namespace Barotrauma
public void DrawToolTip(SpriteBatch spriteBatch)
{
if (!Visible) { return; }
DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect, TooltipRichTextData);
DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect);
}
public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Vector2 pos, List<RichTextData> richTextData = null)
public static void DrawToolTip(SpriteBatch spriteBatch, RichString toolTip, Vector2 pos)
{
if (Tutorials.Tutorial.ContentRunning) { return; }
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial.ContentRunning) { return; }
int width = (int)(400 * GUI.Scale);
int height = (int)(18 * GUI.Scale);
Point padding = new Point((int)(10 * GUI.Scale));
if (toolTipBlock == null || (string)toolTipBlock.userData != toolTip)
if (toolTipBlock == null || (RichString)toolTipBlock.UserData != toolTip)
{
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), richTextData, toolTip, font: GUI.SmallFont, wrap: true, style: "GUIToolTip");
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), toolTip, font: GUIStyle.SmallFont, wrap: true, style: "GUIToolTip");
toolTipBlock.RectTransform.NonScaledSize = new Point(
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
toolTipBlock.userData = toolTip;
(int)(GUIStyle.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(GUIStyle.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
toolTipBlock.UserData = toolTip;
}
toolTipBlock.RectTransform.AbsoluteOffset = pos.ToPoint();
@@ -764,21 +754,21 @@ namespace Barotrauma
toolTipBlock.DrawManually(spriteBatch);
}
public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Rectangle targetElement, List<RichTextData> richTextData = null)
public static void DrawToolTip(SpriteBatch spriteBatch, RichString toolTip, Rectangle targetElement)
{
if (Tutorials.Tutorial.ContentRunning) { return; }
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial.ContentRunning) { return; }
int width = (int)(400 * GUI.Scale);
int height = (int)(18 * GUI.Scale);
Point padding = new Point((int)(10 * GUI.Scale));
if (toolTipBlock == null || (string)toolTipBlock.userData != toolTip)
if (toolTipBlock == null || (RichString)toolTipBlock.UserData != toolTip)
{
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), richTextData, toolTip, font: GUI.SmallFont, wrap: true, style: "GUIToolTip");
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), toolTip, font: GUIStyle.SmallFont, wrap: true, style: "GUIToolTip");
toolTipBlock.RectTransform.NonScaledSize = new Point(
(int)(toolTipBlock.Font.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(toolTipBlock.Font.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
toolTipBlock.userData = toolTip;
toolTipBlock.UserData = toolTip;
}
toolTipBlock.RectTransform.AbsoluteOffset = new Point(targetElement.Center.X, targetElement.Bottom);
@@ -811,7 +801,7 @@ namespace Barotrauma
this.useRectangleFlash = useRectangleFlash;
this.useCircularFlash = useCircularFlash;
this.flashDuration = flashDuration;
flashColor = (color == null) ? GUI.Style.Red : (Color)color;
flashColor = (color == null) ? GUIStyle.Red : (Color)color;
}
public void FadeOut(float duration, bool removeAfter, float wait = 0.0f)
@@ -952,8 +942,7 @@ namespace Barotrauma
ApplySizeRestrictions(style);
}
this.style = style;
styleHierarchy = GUIComponentStyle.ToHierarchy(style);
}
public void ApplySizeRestrictions(GUIComponentStyle style)
@@ -972,11 +961,11 @@ namespace Barotrauma
}
}
public static GUIComponent FromXML(XElement element, RectTransform parent)
public static GUIComponent FromXML(ContentXElement element, RectTransform parent)
{
GUIComponent component = null;
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
if (subElement.Name.ToString().Equals("conditional", StringComparison.OrdinalIgnoreCase) && !CheckConditional(subElement))
{
@@ -1027,7 +1016,7 @@ namespace Barotrauma
if (component != null)
{
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
if (subElement.Name.ToString().Equals("conditional", StringComparison.OrdinalIgnoreCase)) { continue; }
FromXML(subElement, component is GUIListBox listBox ? listBox.Content.RectTransform : component.RectTransform);
@@ -1078,8 +1067,9 @@ namespace Barotrauma
switch (attribute.Name.ToString().ToLowerInvariant())
{
case "language":
string[] languages = element.GetAttributeStringArray(attribute.Name.ToString(), new string[0]);
if (!languages.Any(l => GameMain.Config.Language.Equals(l, StringComparison.OrdinalIgnoreCase))) { return false; }
var languages = element.GetAttributeIdentifierArray(attribute.Name.ToString(), Array.Empty<Identifier>())
.Select(s => new LanguageIdentifier(s));
if (!languages.Any(l => GameSettings.CurrentConfig.Language == l)) { return false; }
break;
case "gameversion":
var version = new Version(attribute.Value);
@@ -1136,23 +1126,12 @@ namespace Barotrauma
if (element.Attribute("color") != null) { color = element.GetAttributeColor("color", Color.White); }
float scale = element.GetAttributeFloat("scale", 1.0f);
bool wrap = element.GetAttributeBool("wrap", true);
Alignment alignment = Alignment.Center;
Enum.TryParse(element.GetAttributeString("alignment", "Center"), out alignment);
ScalableFont font = GUI.Font;
switch (element.GetAttributeString("font", "Font").ToLowerInvariant())
Alignment alignment =
element.GetAttributeEnum("alignment", text.Contains('\n') ? Alignment.Left : Alignment.Center);
GUIFont font;
if (!GUIStyle.Fonts.TryGetValue(element.GetAttributeIdentifier("font", "Font"), out font))
{
case "font":
font = GUI.Font;
break;
case "smallfont":
font = GUI.SmallFont;
break;
case "largefont":
font = GUI.LargeFont;
break;
case "subheading":
font = GUI.SubHeadingFont;
break;
font = GUIStyle.Font;
}
var textBlock = new GUITextBlock(RectTransform.Load(element, parent),
@@ -1265,7 +1244,7 @@ namespace Barotrauma
};
}
private static GUIImage LoadGUIImage(XElement element, RectTransform parent)
private static GUIImage LoadGUIImage(ContentXElement element, RectTransform parent)
{
Sprite sprite;
string url = element.GetAttributeString("url", "");
@@ -1298,11 +1277,11 @@ namespace Barotrauma
return new GUIImage(RectTransform.Load(element, parent), sprite, scaleToFit: true);
}
private static GUIButton LoadAccordion(XElement element, RectTransform parent)
private static GUIButton LoadAccordion(ContentXElement element, RectTransform parent)
{
var button = LoadGUIButton(element, parent);
List<GUIComponent> content = new List<GUIComponent>();
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
var contentElement = FromXML(subElement, parent);
if (contentElement != null)

View File

@@ -9,16 +9,23 @@ namespace Barotrauma
{
struct ContextMenuOption
{
public string Label;
public LocalizedString Label;
public Action OnSelected;
public ContextMenuOption[]? SubOptions;
public bool IsEnabled;
public string Tooltip;
public LocalizedString Tooltip;
// Creates a regular context menu
public ContextMenuOption(string label, bool isEnabled, Action onSelected)
: this(TextManager.Get(label).Fallback(label), isEnabled, onSelected) { }
public ContextMenuOption(Identifier labelTag, bool isEnabled, Action onSelected)
: this(TextManager.Get(labelTag), isEnabled, onSelected) { }
// Creates a regular context menu
public ContextMenuOption(LocalizedString label, bool isEnabled, Action onSelected)
{
Label = TextManager.Get(label, returnNull: true) ?? label;
Label = label;
OnSelected = onSelected;
IsEnabled = isEnabled;
SubOptions = null;
@@ -49,14 +56,14 @@ namespace Barotrauma
/// <param name="header">Header text</param>
/// <param name="style">Background style</param>
/// <param name="options">list of context menu options</param>
public GUIContextMenu(Vector2? position, string header, string style, params ContextMenuOption[] options) : base(style, new RectTransform(Point.Zero, GUI.Canvas))
public GUIContextMenu(Vector2? position, LocalizedString header, string style, params ContextMenuOption[] options) : base(style, new RectTransform(Point.Zero, GUI.Canvas))
{
Vector2 pos = position ?? PlayerInput.MousePosition;
ScalableFont headerFont = GUI.SubHeadingFont;
ScalableFont font = GUI.SmallFont; // font the context menu options use
GUIFont headerFont = GUIStyle.SubHeadingFont;
GUIFont font = GUIStyle.SmallFont; // font the context menu options use
Vector4 padding = new Vector4(4), headerPadding = new Vector4(8);
int horizontalPadding = (int) (padding.X + padding.Z), verticalPadding = (int) (padding.Y + padding.W);
bool hasHeader = !string.IsNullOrWhiteSpace(header);
bool hasHeader = !header.IsNullOrWhiteSpace();
//----------------------------------------------------------------------------------
// Estimate the size of the context menu
@@ -88,14 +95,21 @@ namespace Barotrauma
// Construct the GUI elements
//----------------------------------------------------------------------------------
GUILayoutGroup background = new GUILayoutGroup(new RectTransform(Vector2.One, RectTransform, Anchor.Center));
GUILayoutGroup background = new GUILayoutGroup(new RectTransform(Vector2.One, RectTransform, Anchor.Center))
{
Stretch = true
};
Point listSize = estimatedSize;
if (hasHeader)
{
HeaderLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.2f), background.RectTransform), header, font: headerFont) { Padding = headerPadding };
Point sz = Point.Zero;
InflateSize(ref sz, header, headerFont);
listSize.Y -= sz.Y;
HeaderLabel = new GUITextBlock(new RectTransform(sz, background.RectTransform), header, font: headerFont) { Padding = headerPadding };
}
GUIListBox optionList = new GUIListBox(new RectTransform(new Vector2(1f, hasHeader ? 0.8f : 1f), background.RectTransform), style: null)
GUIListBox optionList = new GUIListBox(new RectTransform(listSize, background.RectTransform), style: null)
{
AutoHideScrollBar = false,
ScrollBarVisible = false,
@@ -111,7 +125,7 @@ namespace Barotrauma
};
Options.Add(option, optionElement);
if (!string.IsNullOrWhiteSpace(option.Tooltip) && optionElement.Enabled)
if (!option.Tooltip.IsNullOrWhiteSpace() && optionElement.Enabled)
{
optionElement.ToolTip = option.Tooltip;
}
@@ -179,7 +193,7 @@ namespace Barotrauma
public static GUIContextMenu CreateContextMenu(params ContextMenuOption[] options) => CreateContextMenu(PlayerInput.MousePosition, string.Empty, null, options);
public static GUIContextMenu CreateContextMenu(Vector2? pos, string header, Color? headerColor, params ContextMenuOption[] options)
public static GUIContextMenu CreateContextMenu(Vector2? pos, LocalizedString header, Color? headerColor, params ContextMenuOption[] options)
{
GUIContextMenu menu = new GUIContextMenu(pos,header, "GUIToolTip", options);
if (headerColor != null)
@@ -209,7 +223,7 @@ namespace Barotrauma
/// <param name="label">String whose size to inflate by</param>
/// <param name="font">What font to use</param>
/// <returns>The size of the text</returns>
private Vector2 InflateSize(ref Point size, string label, ScalableFont font)
private Vector2 InflateSize(ref Point size, LocalizedString label, ScalableFont font)
{
Vector2 textSize = font.MeasureString(label);
size.X = Math.Max((int) Math.Ceiling(textSize.X), size.X);

View File

@@ -55,9 +55,8 @@ namespace Barotrauma
{
get { return listBox.SelectedComponent; }
}
// TODO: fix implicit hiding
public bool Selected
public override bool Selected
{
get
{
@@ -97,7 +96,7 @@ namespace Barotrauma
set { button.TextColor = value; }
}
public override ScalableFont Font
public override GUIFont Font
{
get { return button?.Font ?? base.Font; }
set
@@ -142,13 +141,13 @@ namespace Barotrauma
get { return selectedIndexMultiple; }
}
public string Text
public LocalizedString Text
{
get { return button.Text; }
set { button.Text = value; }
}
public override string ToolTip
public override RichString ToolTip
{
get
{
@@ -162,18 +161,21 @@ namespace Barotrauma
}
}
public GUIDropDown(RectTransform rectT, string text = "", int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false) : base(style, rectT)
public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false, Alignment textAlignment = Alignment.CenterLeft) : base(style, rectT)
{
text ??= new RawLString("");
HoverCursor = CursorState.Hand;
CanBeFocused = true;
this.selectMultiple = selectMultiple;
button = new GUIButton(new RectTransform(Vector2.One, rectT), text, Alignment.CenterLeft, style: "GUIDropDown")
button = new GUIButton(new RectTransform(Vector2.One, rectT), text, textAlignment, style: "GUIDropDown")
{
OnClicked = OnClicked
OnClicked = OnClicked,
TextBlock = { OverflowClip = true }
};
GUI.Style.Apply(button, "", this);
GUIStyle.Apply(button, "", this);
button.TextBlock.SetTextPos();
Anchor listAnchor = dropAbove ? Anchor.TopCenter : Anchor.BottomCenter;
@@ -184,13 +186,13 @@ namespace Barotrauma
Enabled = !selectMultiple
};
if (!selectMultiple) { listBox.OnSelected = SelectItem; }
GUI.Style.Apply(listBox, "GUIListBox", this);
GUI.Style.Apply(listBox.ContentBackground, "GUIListBox", this);
GUIStyle.Apply(listBox, "GUIListBox", this);
GUIStyle.Apply(listBox.ContentBackground, "GUIListBox", this);
if (button.Style.ChildStyles.ContainsKey("dropdownicon"))
if (button.Style.ChildStyles.ContainsKey("dropdownicon".ToIdentifier()))
{
icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), button.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5, 0) }, null, scaleToFit: true);
icon.ApplyStyle(button.Style.ChildStyles["dropdownicon"]);
icon.ApplyStyle(button.Style.ChildStyles["dropdownicon".ToIdentifier()]);
}
currentHighestParent = FindHighestParent();
@@ -244,8 +246,9 @@ namespace Barotrauma
return parentHierarchy.Last();
}
public void AddItem(string text, object userData = null, string toolTip = "")
public void AddItem(LocalizedString text, object userData = null, LocalizedString toolTip = null)
{
toolTip ??= "";
if (selectMultiple)
{
var frame = new GUIFrame(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform)
@@ -261,7 +264,7 @@ namespace Barotrauma
ToolTip = toolTip,
OnSelected = (GUITickBox tb) =>
{
List<string> texts = new List<string>();
List<LocalizedString> texts = new List<LocalizedString>();
selectedDataMultiple.Clear();
selectedIndexMultiple.Clear();
int i = 0;
@@ -276,7 +279,7 @@ namespace Barotrauma
}
i++;
}
button.Text = string.Join(", ", texts);
button.Text = LocalizedString.Join(", ", texts);
// TODO: The callback is called at least twice, remove this?
OnSelected?.Invoke(tb.Parent, tb.Parent.UserData);
return true;
@@ -311,13 +314,12 @@ namespace Barotrauma
foreach (GUIComponent child in ListBox.Content.Children)
{
var tickBox = child.GetChild<GUITickBox>();
if (obj == child.UserData) { tickBox.Selected = true; }
if (Equals(obj, child.UserData)) { tickBox.Selected = true; }
}
}
else
{
GUITextBlock textBlock = component as GUITextBlock;
if (textBlock == null)
if (!(component is GUITextBlock textBlock))
{
textBlock = component.GetChild<GUITextBlock>();
if (textBlock is null && !AllowNonText) { return false; }
@@ -368,8 +370,9 @@ namespace Barotrauma
Dropped = !Dropped;
if (Dropped && Enabled)
{
OnDropped?.Invoke(this, userData);
OnDropped?.Invoke(this, UserData);
listBox.UpdateScrollBarSize();
listBox.UpdateDimensions();
GUI.KeyboardDispatcher.Subscriber = this;
}

View File

@@ -151,8 +151,13 @@ namespace Barotrauma
loadingTextures = true;
loading = true;
TaskPool.Add("LoadTextureAsync",
LoadTextureAsync(), (Task) =>
LoadTextureAsync(), task =>
{
if (task.Exception != null)
{
var innerMost = task.Exception.GetInnermost();
DebugConsole.ThrowError($"Failed to load \"{Sprite.FilePath}\"", innerMost);
}
loading = false;
lazyLoaded = true;
RectTransform.SizeChanged += RecalculateScale;
@@ -177,6 +182,7 @@ namespace Barotrauma
spriteBatch.Begin(blendState: BlendState, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
}
var style = Style;
if (style != null)
{
foreach (UISprite uiSprite in style.Sprites[State])
@@ -193,7 +199,7 @@ namespace Barotrauma
}
}
}
else if (sprite?.Texture != null)
else if (sprite?.Texture is { IsDisposed: false })
{
spriteBatch.Draw(sprite.Texture, Rect.Center.ToVector2(), sourceRect, currentColor * (currentColor.A / 255.0f), Rotation, origin,
Scale, SpriteEffects, 0.0f);
@@ -231,7 +237,7 @@ namespace Barotrauma
{
wait = activeTextureLoads.Contains(Sprite.FullPath);
}
}
}
}
try
{

View File

@@ -92,29 +92,19 @@ namespace Barotrauma
foreach (RectTransform child in RectTransform.Children)
{
if (child.GUIComponent.IgnoreLayoutGroups) { continue; }
if (child.ScaleBasis == ScaleBasis.BothHeight) { child.MinSize = new Point(child.Rect.Height, child.MinSize.Y); }
if (child.ScaleBasis == ScaleBasis.BothWidth) { child.MinSize = new Point(child.MinSize.X, child.Rect.Width); }
if (child.ScaleBasis == ScaleBasis.Smallest)
switch (child.ScaleBasis)
{
if (Rect.Width < Rect.Height)
{
child.MinSize = new Point(child.MinSize.X, child.Rect.Width);
}
else
{
child.MinSize = new Point(child.Rect.Height, child.MinSize.Y);
}
}
if (child.ScaleBasis == ScaleBasis.Largest)
{
if (Rect.Width > Rect.Height)
{
child.MinSize = new Point(child.MinSize.X, child.Rect.Width);
}
else
{
child.MinSize = new Point(child.Rect.Height, child.MinSize.Y);
}
case ScaleBasis.BothHeight:
case ScaleBasis.Smallest when Rect.Height <= Rect.Width:
case ScaleBasis.Largest when Rect.Height > Rect.Width:
child.MinSize = new Point((int)((child.Rect.Height * child.RelativeSize.X) / child.RelativeSize.Y), child.MinSize.Y);
break;
case ScaleBasis.BothWidth:
case ScaleBasis.Smallest when Rect.Width <= Rect.Height:
case ScaleBasis.Largest when Rect.Width > Rect.Height:
child.MinSize = new Point(child.MinSize.X, (int)((child.Rect.Width * child.RelativeSize.Y) / child.RelativeSize.X));
break;
}
}
@@ -144,42 +134,65 @@ namespace Barotrauma
foreach (var child in RectTransform.Children)
{
if (child.GUIComponent.IgnoreLayoutGroups) { continue; }
float currentStretchFactor = child.ScaleBasis == ScaleBasis.Normal ? stretchFactor : 1.0f;
child.SetPosition(childAnchor);
void advancePositionsAndCalculateChildSizes(
ref int childNonScaledSize,
ref float childRelativeSize,
int childMinSize,
int childMaxSize,
int childRectSize,
int selfRectSize)
{
if (child.IsFixedSize)
{
absPos += childNonScaledSize + absoluteSpacing;
}
else
{
absPos += (int)Math.Round(MathHelper.Clamp(childRectSize * currentStretchFactor, childMinSize, childMaxSize) + (absoluteSpacing * currentStretchFactor));
if (stretch)
{
float relativeSize =
MathF.Round(childRelativeSize * currentStretchFactor * selfRectSize) / selfRectSize;
childRelativeSize = relativeSize;
}
}
}
Point childNonScaledSize = child.NonScaledSize;
Vector2 childRelativeSize = child.RelativeSize;
if (isHorizontal)
{
child.RelativeOffset = new Vector2(relPos, child.RelativeOffset.Y);
child.AbsoluteOffset = new Point(absPos, child.AbsoluteOffset.Y);
if (child.IsFixedSize)
{
absPos += child.NonScaledSize.X + absoluteSpacing;
}
else
{
absPos += (int)(MathHelper.Clamp(child.Rect.Width * stretchFactor, child.MinSize.X, child.MaxSize.X) + (absoluteSpacing * stretchFactor));
if (stretch)
{
child.RelativeSize = new Vector2(child.RelativeSize.X * stretchFactor, child.RelativeSize.Y);
}
}
advancePositionsAndCalculateChildSizes(
ref childNonScaledSize.X,
ref childRelativeSize.X,
child.MinSize.X,
child.MaxSize.X,
child.Rect.Width,
Rect.Width);
}
else
{
child.RelativeOffset = new Vector2(child.RelativeOffset.X, relPos);
child.AbsoluteOffset = new Point(child.AbsoluteOffset.X, absPos);
if (child.IsFixedSize)
{
absPos += child.NonScaledSize.Y + absoluteSpacing;
}
else
{
absPos += (int)(MathHelper.Clamp(child.Rect.Height * stretchFactor, child.MinSize.Y, child.MaxSize.Y) + (absoluteSpacing * stretchFactor));
if (stretch)
{
child.RelativeSize = new Vector2(child.RelativeSize.X, child.RelativeSize.Y * stretchFactor);
}
}
advancePositionsAndCalculateChildSizes(
ref childNonScaledSize.Y,
ref childRelativeSize.Y,
child.MinSize.Y,
child.MaxSize.Y,
child.Rect.Height,
Rect.Height);
}
child.NonScaledSize = childNonScaledSize;
child.RelativeSize = childRelativeSize;
relPos += relativeSpacing * stretchFactor;
if (isHorizontal) { relPos = MathF.Round(relPos * Rect.Width) / Rect.Width; }
else { relPos = MathF.Round(relPos * Rect.Height) / Rect.Height; }
}
needsToRecalculate = false;
}

View File

@@ -148,7 +148,11 @@ namespace Barotrauma
}
// TODO: fix implicit hiding
public bool Selected { get; set; }
public override bool Selected
{
get { return isSelected; }
set { isSelected = value; }
}
public IReadOnlyList<GUIComponent> AllSelected => selected;
@@ -328,7 +332,7 @@ namespace Barotrauma
};
if (style != null)
{
GUI.Style.Apply(ContentBackground, "", this);
GUIStyle.Apply(ContentBackground, "", this);
}
if (color.HasValue)
{
@@ -398,8 +402,7 @@ namespace Barotrauma
int i = 0;
foreach (GUIComponent child in children)
{
if ((child.UserData != null && child.UserData.Equals(userData)) ||
(child.UserData == null && userData == null))
if (Equals(child.UserData, userData))
{
Select(i, force, autoScroll);
if (!SelectMultiple) { return; }
@@ -435,7 +438,7 @@ namespace Barotrauma
Vector2 topOffset = CalculateTopOffset();
int x = (int)topOffset.X;
int y = (int)topOffset.Y;
for (int i = 0; i < Content.CountChildren; i++)
{
GUIComponent child = Content.GetChild(i);
@@ -509,23 +512,36 @@ namespace Barotrauma
}
/// <summary>
/// Scrolls the list to the specific element, currently only works when smooth scrolling and PadBottom are enabled.
/// Scrolls the list to the specific element.
/// </summary>
/// <param name="component"></param>
public void ScrollToElement(GUIComponent component)
public void ScrollToElement(GUIComponent component, bool playSound = true)
{
SoundPlayer.PlayUISound(GUISoundType.Click);
if (playSound) { SoundPlayer.PlayUISound(GUISoundType.Click); }
List<GUIComponent> children = Content.Children.ToList();
int index = children.IndexOf(component);
if (index < 0) { return; }
void performScroll(GUIComponent c)
{
if (SmoothScroll && PadBottom)
{
scrollToElement = c;
}
else
{
float diff = isHorizontal ? c.Rect.X - Content.Rect.X : c.Rect.Y - Content.Rect.Y;
ScrollBar.BarScroll += diff / TotalSize;
}
}
if (!Content.Children.Contains(component) || !component.Visible)
{
scrollToElement = null;
performScroll(null);
}
else
{
scrollToElement = component;
performScroll(component);
}
}
@@ -1215,6 +1231,20 @@ namespace Barotrauma
i++;
}
if (isDraggingElement && CurrentDragMode == DragMode.DragOutsideBox && HideDraggedElement)
{
Rectangle drawRect = DraggedElement.Rect;
int draggedElementIndex = Content.GetChildIndex(DraggedElement);
CalculateChildrenOffsets((index, point) =>
{
if (draggedElementIndex == index)
{
drawRect.Location = Content.Rect.Location + point;
}
});
GUI.DrawRectangle(spriteBatch, drawRect, Color.White * 0.5f, thickness: 2f);
}
if (HideChildrenOutsideFrame)
{
spriteBatch.End();

View File

@@ -10,7 +10,8 @@ namespace Barotrauma
{
public class GUIMessageBox : GUIFrame
{
public static List<GUIComponent> MessageBoxes = new List<GUIComponent>();
#warning TODO: change this to List<GUIMessageBox> and fix incorrect uses of this list
public readonly static List<GUIComponent> MessageBoxes = new List<GUIComponent>();
private static int DefaultWidth
{
get { return Math.Max(400, (int)(400 * (GameMain.GraphicsWidth / GUI.ReferenceResolution.X))); }
@@ -70,14 +71,14 @@ namespace Barotrauma
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
public GUIMessageBox(string headerText, string text, Vector2? relativeSize = null, Point? minSize = null)
: this(headerText, text, new string[] { "OK" }, relativeSize, minSize)
public GUIMessageBox(LocalizedString headerText, LocalizedString text, Vector2? relativeSize = null, Point? minSize = null)
: this(headerText, text, new LocalizedString[] { "OK" }, relativeSize, minSize)
{
this.Buttons[0].OnClicked = Close;
}
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null, bool parseRichText = false)
: base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
public GUIMessageBox(RichString headerText, RichString text, LocalizedString[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null)
: base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUIStyle.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
{
int width = (int)(DefaultWidth * type switch
{
@@ -125,23 +126,24 @@ namespace Barotrauma
InnerFrame.RectTransform.ScreenSpaceOffset = new Point(-offset, offset);
CanBeFocused = false;
}
GUI.Style.Apply(InnerFrame, "", this);
GUIStyle.Apply(InnerFrame, "", this);
this.type = type;
Tag = tag;
#warning TODO: These should be broken into separate methods at least
if (type == Type.Default || type == Type.Vote)
{
Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform),
headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Header, "", this);
headerText, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
GUIStyle.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text))
if (!text.IsNullOrWhiteSpace())
{
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Text, "", this);
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
GUIStyle.Apply(Text, "", this);
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height);
Text.RectTransform.IsFixedSize = true;
@@ -154,7 +156,7 @@ namespace Barotrauma
};
int buttonSize = 35;
var buttonStyle = GUI.Style.GetComponentStyle("GUIButton");
var buttonStyle = GUIStyle.GetComponentStyle("GUIButton");
if (buttonStyle != null && buttonStyle.Height.HasValue)
{
buttonSize = buttonStyle.Height.Value;
@@ -189,7 +191,7 @@ namespace Barotrauma
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
CanBeFocused = false;
AutoClose = true;
GUI.Style.Apply(InnerFrame, "", this);
GUIStyle.Apply(InnerFrame, "", this);
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
@@ -219,11 +221,11 @@ namespace Barotrauma
};
InputType? closeInput = null;
if (GameMain.Config.KeyBind(InputType.Use).MouseButton == MouseButton.None)
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Use].MouseButton == MouseButton.None)
{
closeInput = InputType.Use;
}
else if (GameMain.Config.KeyBind(InputType.Select).MouseButton == MouseButton.None)
else if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select].MouseButton == MouseButton.None)
{
closeInput = InputType.Select;
}
@@ -236,24 +238,24 @@ namespace Barotrauma
{
GUIButton btn = component as GUIButton;
btn?.OnClicked(btn, btn.UserData);
btn?.Flash(GUI.Style.Green);
btn?.Flash(GUIStyle.Green);
}
};
}
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Header, "", this);
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
GUIStyle.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text))
if (!text.IsNullOrWhiteSpace())
{
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText);
GUI.Style.Apply(Text, "", this);
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
GUIStyle.Apply(Text, "", this);
Content.Recalculate();
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height);
Text.RectTransform.IsFixedSize = true;
if (string.IsNullOrWhiteSpace(headerText))
if (headerText.IsNullOrWhiteSpace())
{
Content.ChildAnchor = Anchor.Center;
}
@@ -275,7 +277,7 @@ namespace Barotrauma
else if (type == Type.Hint)
{
CanBeFocused = false;
GUI.Style.Apply(InnerFrame, "", this);
GUIStyle.Apply(InnerFrame, "", this);
Point absoluteSpacing = GUIStyle.ItemFrameMargin.Multiply(1.0f / 5.0f);
var verticalLayoutGroup = new GUILayoutGroup(new RectTransform(GetVerticalLayoutGroupSize(), parent: InnerFrame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
@@ -353,18 +355,18 @@ namespace Barotrauma
};
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
GUI.Style.Apply(Header, "", this);
GUIStyle.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
if (!string.IsNullOrWhiteSpace(text))
if (!text.IsNullOrWhiteSpace())
{
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
GUI.Style.Apply(Text, "", this);
GUIStyle.Apply(Text, "", this);
Content.Recalculate();
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height);
Text.RectTransform.IsFixedSize = true;
if (string.IsNullOrWhiteSpace(headerText))
if (headerText.IsNullOrWhiteSpace())
{
Header.RectTransform.Parent = null;
Content.ChildAnchor = Anchor.Center;
@@ -410,7 +412,7 @@ namespace Barotrauma
/// <summary>
/// Use to create a message box of Hint type
/// </summary>
public GUIMessageBox(string hintIdentifier, string text, Sprite icon) : this("", text, new string[0], textAlignment: Alignment.CenterLeft, type: Type.Hint, icon: icon)
public GUIMessageBox(Identifier hintIdentifier, LocalizedString text, Sprite icon) : this("", text, Array.Empty<LocalizedString>(), textAlignment: Alignment.CenterLeft, type: Type.Hint, icon: icon)
{
if (InnerFrame.FindChild("dontshowagain", recursive: true) is GUITickBox dontShowAgainTickBox)
{

View File

@@ -12,9 +12,12 @@ namespace Barotrauma
Int, Float
}
public delegate void OnValueEnteredHandler(GUINumberInput numberInput);
public OnValueEnteredHandler OnValueEntered;
public delegate void OnValueChangedHandler(GUINumberInput numberInput);
public OnValueChangedHandler OnValueChanged;
public GUITextBox TextBox { get; private set; }
public GUIButton PlusButton { get; private set; }
@@ -162,7 +165,7 @@ namespace Barotrauma
}
}
public override ScalableFont Font
public override GUIFont Font
{
get
{
@@ -209,6 +212,8 @@ namespace Barotrauma
{
ClampFloatValue();
}
OnValueEntered?.Invoke(this);
};
TextBox.OnEnterPressed += (textBox, text) =>
{
@@ -220,12 +225,14 @@ namespace Barotrauma
{
ClampFloatValue();
}
OnValueEntered?.Invoke(this);
return true;
};
var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null);
PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
GUI.Style.Apply(PlusButton, "PlusButton", this);
GUIStyle.Apply(PlusButton, "PlusButton", this);
PlusButton.OnButtonDown += () =>
{
pressedTimer = pressedDelay;
@@ -246,7 +253,7 @@ namespace Barotrauma
};
MinusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform, Anchor.BottomRight), style: null);
GUI.Style.Apply(MinusButton, "MinusButton", this);
GUIStyle.Apply(MinusButton, "MinusButton", this);
MinusButton.OnButtonDown += () =>
{
pressedTimer = pressedDelay;

View File

@@ -0,0 +1,395 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma
{
public abstract class GUIPrefab : Prefab
{
public GUIPrefab(ContentXElement element, UIStyleFile file) : base(file, element) { }
protected override Identifier DetermineIdentifier(XElement element)
{
return element.NameAsIdentifier();
}
}
public abstract class GUISelector<T> where T : GUIPrefab
{
public readonly PrefabSelector<T> Prefabs = new PrefabSelector<T>();
public readonly Identifier Identifier;
public GUISelector(string identifier)
{
Identifier = identifier.ToIdentifier();
}
}
public class GUIFontPrefab : GUIPrefab
{
private readonly ContentXElement element;
private ScalableFont font;
public ScalableFont Font
{
get
{
if (Language != GameSettings.CurrentConfig.Language) { LoadFont(); }
return font;
}
}
private ScalableFont cjkFont;
public ScalableFont CjkFont
{
get
{
if (Language != GameSettings.CurrentConfig.Language) { LoadFont(); }
if (font.IsCJK) { return font; }
return cjkFont;
}
}
public LanguageIdentifier Language { get; private set; }
public GUIFontPrefab(ContentXElement element, UIStyleFile file) : base(element, file)
{
this.element = element;
LoadFont();
}
public void LoadFont()
{
string fontPath = GetFontFilePath(element);
uint size = GetFontSize(element);
bool dynamicLoading = GetFontDynamicLoading(element);
bool isCJK = GetIsCJK(element);
font?.Dispose();
cjkFont?.Dispose();
font = new ScalableFont(fontPath, size, GameMain.Instance.GraphicsDevice, dynamicLoading, isCJK)
{
ForceUpperCase = element.GetAttributeBool("forceuppercase", false)
};
if (!isCJK)
{
cjkFont = ExtractCjkFont(element)
?? new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
font.Size, GameMain.Instance.GraphicsDevice, dynamicLoading: true, isCJK: true);
cjkFont.ForceUpperCase = font.ForceUpperCase;
}
Language = GameSettings.CurrentConfig.Language;
}
public override void Dispose()
{
font?.Dispose(); font = null;
cjkFont?.Dispose(); cjkFont = null;
}
private ScalableFont ExtractCjkFont(ContentXElement element)
{
foreach (var subElement in element.Elements().Reverse())
{
if (subElement.NameAsIdentifier() != "override") { continue; }
if (subElement.GetAttributeBool("iscjk", false))
{
return new ScalableFont(subElement, GameMain.Instance.GraphicsDevice);
}
}
return null;
}
private string GetFontFilePath(ContentXElement element)
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
{
return subElement.GetAttributeContentPath("file")?.Value;
}
}
return element.GetAttributeContentPath("file")?.Value;
}
private uint GetFontSize(XElement element, uint defaultSize = 14)
{
//check if any of the language override fonts want to override the font size as well
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
{
uint overrideFontSize = GetFontSize(subElement, 0);
if (overrideFontSize > 0) { return (uint)Math.Round(overrideFontSize * GameSettings.CurrentConfig.Graphics.TextScale); }
}
}
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("size", StringComparison.OrdinalIgnoreCase)) { continue; }
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
{
return (uint)Math.Round(subElement.GetAttributeInt("size", 14) * GameSettings.CurrentConfig.Graphics.TextScale);
}
}
return (uint)Math.Round(defaultSize * GameSettings.CurrentConfig.Graphics.TextScale);
}
private bool GetFontDynamicLoading(XElement element)
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
{
return subElement.GetAttributeBool("dynamicloading", false);
}
}
return element.GetAttributeBool("dynamicloading", false);
}
private bool GetIsCJK(XElement element)
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
{
return subElement.GetAttributeBool("iscjk", false);
}
}
return element.GetAttributeBool("iscjk", false);
}
}
public class GUIFont : GUISelector<GUIFontPrefab>
{
public GUIFont(string identifier) : base(identifier) { }
public bool HasValue => Prefabs.Any();
public ScalableFont Value => Prefabs.ActivePrefab.Font;
public static implicit operator ScalableFont(GUIFont reference) => reference.Value;
public bool ForceUpperCase => HasValue && Value.ForceUpperCase;
public uint Size => HasValue ? Value.Size : 0;
private ScalableFont GetFontForStr(LocalizedString str) => GetFontForStr(str.Value);
private ScalableFont GetFontForStr(string str) =>
TextManager.IsCJK(str) ? Prefabs.ActivePrefab.CjkFont : Prefabs.ActivePrefab.Font;
public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
{
DrawString(sb, text.Value, position, color, rotation, origin, scale, se, layerDepth);
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
{
GetFontForStr(text).DrawString(sb, text, position, color, rotation, origin, scale, se, layerDepth);
}
public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, Alignment alignment = Alignment.TopLeft)
{
DrawString(sb, text.Value, position, color, rotation, origin, scale, se, layerDepth, alignment);
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
{
GetFontForStr(text).DrawString(sb, text, position, color, rotation, origin, scale, se, layerDepth, alignment, forceUpperCase);
}
public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit, bool italics = false)
{
DrawString(sb, text.Value, position, color, forceUpperCase, italics);
}
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit, bool italics = false)
{
GetFontForStr(text).DrawString(sb, text, position, color, forceUpperCase, italics);
}
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, in ImmutableArray<RichTextData>? richTextData, int rtdOffset = 0, Alignment alignment = Alignment.TopLeft, ForceUpperCase forceUpperCase = Barotrauma.ForceUpperCase.Inherit)
{
GetFontForStr(text).DrawStringWithColors(sb, text, position, color, rotation, origin, scale, se, layerDepth, richTextData, rtdOffset, alignment, forceUpperCase);
}
public Vector2 MeasureString(LocalizedString str, bool removeExtraSpacing = false)
{
return GetFontForStr(str).MeasureString(str, removeExtraSpacing);
}
public Vector2 MeasureChar(char c)
{
return GetFontForStr($"{c}").MeasureChar(c);
}
public string WrapText(string text, float width)
=> GetFontForStr(text).WrapText(text, width);
public string WrapText(string text, float width, int requestCharPos, out Vector2 requestedCharPos)
=> GetFontForStr(text).WrapText(text, width, requestCharPos, out requestedCharPos);
public string WrapText(string text, float width, out Vector2[] allCharPositions)
=> GetFontForStr(text).WrapText(text, width, out allCharPositions);
public float LineHeight => Value.LineHeight;
}
public class GUIColorPrefab : GUIPrefab
{
public readonly Color Color;
public GUIColorPrefab(ContentXElement element, UIStyleFile file) : base(element, file)
{
Color = element.GetAttributeColor("color", Color.White);
}
public override void Dispose() { }
}
public class GUIColor : GUISelector<GUIColorPrefab>
{
public GUIColor(string identifier) : base(identifier) { }
public Color Value
{
get
{
return Prefabs.ActivePrefab.Color;
}
}
public static implicit operator Color(GUIColor reference) => reference.Value;
public static Color operator*(GUIColor value, float scale)
{
return value.Value * scale;
}
}
public class GUISpritePrefab : GUIPrefab
{
public readonly UISprite Sprite;
public GUISpritePrefab(ContentXElement element, UIStyleFile file) : base(element, file)
{
Sprite = new UISprite(element);
}
public override void Dispose()
{
Sprite.Sprite.Remove();
}
}
public class GUISprite : GUISelector<GUISpritePrefab>
{
public GUISprite(string identifier) : base(identifier) { }
public UISprite Value
{
get
{
return Prefabs.ActivePrefab.Sprite;
}
}
public static implicit operator UISprite(GUISprite reference) => reference.Value;
public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None)
{
Value.Draw(spriteBatch, rect, color, spriteEffects);
}
}
public class GUISpriteSheetPrefab : GUIPrefab
{
public readonly SpriteSheet SpriteSheet;
public GUISpriteSheetPrefab(ContentXElement element, UIStyleFile file) : base(element, file)
{
SpriteSheet = new SpriteSheet(element);
}
public override void Dispose()
{
SpriteSheet.Remove();
}
}
public class GUISpriteSheet : GUISelector<GUISpriteSheetPrefab>
{
public GUISpriteSheet(string identifier) : base(identifier) { }
public SpriteSheet Value
{
get
{
return Prefabs.ActivePrefab.SpriteSheet;
}
}
public int FrameCount => Value.FrameCount;
public Point FrameSize => Value.FrameSize;
public void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate = 0, float scale = 1, SpriteEffects spriteEffects = SpriteEffects.None)
{
Value.Draw(spriteBatch, pos, rotate, scale, spriteEffects);
}
public void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate = 0, float scale = 1, SpriteEffects spriteEffects = SpriteEffects.None, float? depth = null)
{
Value.Draw(spriteBatch, pos, color, origin, rotate, scale, spriteEffects, depth);
}
public void Draw(ISpriteBatch spriteBatch, int spriteIndex, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffects = SpriteEffects.None, float? depth = null)
{
Value.Draw(spriteBatch, spriteIndex, pos, color, origin, rotate, scale, spriteEffects, depth);
}
public static implicit operator SpriteSheet(GUISpriteSheet reference) => reference.Value;
}
public class GUICursorPrefab : GUIPrefab
{
public readonly Sprite[] Sprites;
public GUICursorPrefab(ContentXElement element, UIStyleFile file) : base(element, file)
{
Sprites = new Sprite[Enum.GetValues(typeof(CursorState)).Length];
foreach (var subElement in element.Elements())
{
CursorState state = subElement.GetAttributeEnum("state", CursorState.Default);
Sprites[(int)state] = new Sprite(subElement);
}
}
public override void Dispose()
{
foreach (var sprite in Sprites)
{
sprite?.Remove();
}
}
}
public class GUICursor : GUISelector<GUICursorPrefab>
{
public GUICursor(string identifier) : base(identifier) { }
public Sprite this[CursorState k] => Prefabs.ActivePrefab.Sprites[(int)k];
}
}

View File

@@ -47,9 +47,9 @@ namespace Barotrauma
}
isHorizontal = (Rect.Width > Rect.Height);
frame = new GUIFrame(new RectTransform(Vector2.One, rectT));
GUI.Style.Apply(frame, "", this);
GUIStyle.Apply(frame, "", this);
slider = new GUIFrame(new RectTransform(Vector2.One, rectT));
GUI.Style.Apply(slider, "Slider", this);
GUIStyle.Apply(slider, "Slider", this);
this.showFrame = showFrame;
this.barSize = barSize;
Enabled = true;
@@ -62,10 +62,10 @@ namespace Barotrauma
public Rectangle GetSliderRect(float fillAmount)
{
Rectangle sliderArea = new Rectangle(
frame.Rect.X + (int)style.Padding.X,
frame.Rect.Y + (int)style.Padding.Y,
(int)(frame.Rect.Width - style.Padding.X - style.Padding.Z),
(int)(frame.Rect.Height - style.Padding.Y - style.Padding.W));
frame.Rect.X + (int)Style.Padding.X,
frame.Rect.Y + (int)Style.Padding.Y,
(int)(frame.Rect.Width - Style.Padding.X - Style.Padding.Z),
(int)(frame.Rect.Height - Style.Padding.Y - Style.Padding.W));
Vector4 sliceBorderSizes = Vector4.Zero;
if (slider.sprites.ContainsKey(slider.State) && (slider.sprites[slider.State].First()?.Slice ?? false))
@@ -116,10 +116,10 @@ namespace Barotrauma
var sliderRect = GetSliderRect(barSize);
slider.RectTransform.AbsoluteOffset = new Point((int)style.Padding.X, (int)style.Padding.Y);
slider.RectTransform.AbsoluteOffset = new Point((int)Style.Padding.X, (int)Style.Padding.Y);
slider.RectTransform.MaxSize = new Point(
(int)(Rect.Width - style.Padding.X + style.Padding.Z),
(int)(Rect.Height - style.Padding.Y + style.Padding.W));
(int)(Rect.Width - Style.Padding.X + Style.Padding.Z),
(int)(Rect.Height - Style.Padding.Y + Style.Padding.W));
frame.Visible = showFrame;
slider.Visible = BarSize > 0.0f;

View File

@@ -1,6 +1,5 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma
@@ -29,7 +28,7 @@ namespace Barotrauma
public bool IsBooleanSwitch;
public override string ToolTip
public override RichString ToolTip
{
get { return base.ToolTip; }
set
@@ -203,7 +202,7 @@ namespace Barotrauma
CanBeFocused = true;
this.isHorizontal = isHorizontal ?? (Rect.Width > Rect.Height);
Frame = new GUIFrame(new RectTransform(Vector2.One, rectT));
GUI.Style.Apply(Frame, IsHorizontal ? "GUIFrameHorizontal" : "GUIFrameVertical", this);
GUIStyle.Apply(Frame, IsHorizontal ? "GUIFrameHorizontal" : "GUIFrameVertical", this);
this.barSize = barSize;
Bar = new GUIButton(new RectTransform(Vector2.One, rectT, IsHorizontal ? Anchor.CenterLeft : Anchor.TopCenter), color: color, style: null);
@@ -224,7 +223,7 @@ namespace Barotrauma
break;
}
GUI.Style.Apply(Bar, IsHorizontal ? "GUIButtonHorizontal" : "GUIButtonVertical", this);
GUIStyle.Apply(Bar, IsHorizontal ? "GUIButtonHorizontal" : "GUIButtonVertical", this);
Bar.OnPressed = SelectBar;
enabled = true;
UpdateRect();
@@ -313,6 +312,7 @@ namespace Barotrauma
MoveButton(new Vector2(
Math.Sign(PlayerInput.MousePosition.X - Bar.Rect.Center.X) * Bar.Rect.Width * barScale,
Math.Sign(PlayerInput.MousePosition.Y - Bar.Rect.Center.Y) * Bar.Rect.Height * barScale));
OnReleased?.Invoke(this, BarScroll);
}
}
}

View File

@@ -1,523 +1,202 @@
using Barotrauma.Extensions;
using System;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
namespace Barotrauma
{
public class GUIStyle
public static class GUIStyle
{
private Dictionary<string, GUIComponentStyle> componentStyles;
private readonly XElement configElement;
private GraphicsDevice graphicsDevice;
private ScalableFont defaultFont;
public ScalableFont Font { get; private set; }
public ScalableFont GlobalFont { get; private set; }
public ScalableFont UnscaledSmallFont { get; private set; }
public ScalableFont SmallFont { get; private set; }
public ScalableFont LargeFont { get; private set; }
public ScalableFont SubHeadingFont { get; private set; }
public ScalableFont DigitalFont { get; private set; }
public ScalableFont HotkeyFont { get; private set; }
public ScalableFont MonospacedFont { get; private set; }
public Dictionary<ScalableFont, bool> ForceFontUpperCase
public readonly static ImmutableDictionary<Identifier, GUIFont> Fonts;
public readonly static ImmutableDictionary<Identifier, GUISprite> Sprites;
public readonly static ImmutableDictionary<Identifier, GUISpriteSheet> SpriteSheets;
public readonly static ImmutableDictionary<Identifier, GUIColor> Colors;
static GUIStyle()
{
get;
private set;
} = new Dictionary<ScalableFont, bool>();
var guiClassProperties = typeof(GUIStyle).GetFields(BindingFlags.Public | BindingFlags.Static);
public readonly Sprite[] CursorSprite = new Sprite[7];
ImmutableDictionary<Identifier, T> getPropertiesOfType<T>() where T : class
{
return guiClassProperties
.Where(p => p.FieldType == typeof(T))
.Select(p => (p.Name.ToIdentifier(), p.GetValue(null) as T))
.ToImmutableDictionary();
}
public UISprite RadiationSprite { get; private set; }
public SpriteSheet RadiationAnimSpriteSheet { get; private set; }
Fonts = getPropertiesOfType<GUIFont>();
Sprites = getPropertiesOfType<GUISprite>();
SpriteSheets = getPropertiesOfType<GUISpriteSheet>();
Colors = getPropertiesOfType<GUIColor>();
}
public SpriteSheet SavingIndicator { get; private set; }
public readonly static PrefabCollection<GUIComponentStyle> ComponentStyles = new PrefabCollection<GUIComponentStyle>();
public UISprite UIGlow { get; private set; }
public readonly static GUIFont Font = new GUIFont("Font");
public readonly static GUIFont GlobalFont = new GUIFont("GlobalFont");
public readonly static GUIFont UnscaledSmallFont = new GUIFont("UnscaledSmallFont");
public readonly static GUIFont SmallFont = new GUIFont("SmallFont");
public readonly static GUIFont LargeFont = new GUIFont("LargeFont");
public readonly static GUIFont SubHeadingFont = new GUIFont("SubHeadingFont");
public readonly static GUIFont DigitalFont = new GUIFont("DigitalFont");
public readonly static GUIFont HotkeyFont = new GUIFont("HotkeyFont");
public readonly static GUIFont MonospacedFont = new GUIFont("MonospacedFont");
public UISprite PingCircle { get; private set; }
public readonly static GUICursor CursorSprite = new GUICursor("Cursor");
public UISprite YouAreHereCircle { get; private set; }
public readonly static GUISprite SubmarineLocationIcon = new GUISprite("SubmarineLocationIcon");
public readonly static GUISprite Arrow = new GUISprite("Arrow");
public readonly static GUISprite SpeechBubbleIcon = new GUISprite("SpeechBubbleIcon");
public readonly static GUISprite BrokenIcon = new GUISprite("BrokenIcon");
public readonly static GUISprite YouAreHereCircle = new GUISprite("YouAreHereCircle");
public UISprite UIGlowCircular { get; private set; }
public readonly static GUISprite Radiation = new GUISprite("Radiation");
public readonly static GUISpriteSheet RadiationAnimSpriteSheet = new GUISpriteSheet("RadiationAnimSpriteSheet");
public UISprite UIGlowSolidCircular { get; private set; }
public UISprite UIThermalGlow { get; private set; }
public readonly static GUISpriteSheet SavingIndicator = new GUISpriteSheet("SavingIndicator");
public readonly static GUISpriteSheet GenericThrobber = new GUISpriteSheet("GenericThrobber");
public UISprite ButtonPulse { get; private set; }
public readonly static GUISprite UIGlow = new GUISprite("UIGlow");
public readonly static GUISprite TalentGlow = new GUISprite("TalentGlow");
public readonly static GUISprite PingCircle = new GUISprite("PingCircle");
public readonly static GUISprite UIGlowCircular = new GUISprite("UIGlowCircular");
public readonly static GUISprite UIGlowSolidCircular = new GUISprite("UIGlowSolidCircular");
public readonly static GUISprite UIThermalGlow = new GUISprite("UIGlowSolidCircular");
public readonly static GUISprite ButtonPulse = new GUISprite("ButtonPulse");
public readonly static GUISprite WalletPortraitBG = new GUISprite("WalletPortraitBG");
public readonly static GUISprite CrewWalletIconSmall = new GUISprite("CrewWalletIconSmall");
public SpriteSheet FocusIndicator { get; private set; }
public readonly static GUISprite EndRoundButtonPulse = new GUISprite("EndRoundButtonPulse");
public UISprite IconOverflowIndicator { get; private set; }
public readonly static GUISpriteSheet FocusIndicator = new GUISpriteSheet("FocusIndicator");
public readonly static GUISprite IconOverflowIndicator = new GUISprite("IconOverflowIndicator");
/// <summary>
/// General green color used for elements whose colors are set from code
/// </summary>
public Color Green { get; private set; } = Color.LightGreen;
public readonly static GUIColor Green = new GUIColor("Green");
/// <summary>
/// General red color used for elements whose colors are set from code
/// </summary>
public Color Orange { get; private set; } = Color.Orange;
public readonly static GUIColor Orange = new GUIColor("Orange");
/// <summary>
/// General red color used for elements whose colors are set from code
/// </summary>
public Color Red { get; private set; } = Color.Red;
public readonly static GUIColor Red = new GUIColor("Red");
/// <summary>
/// General blue color used for elements whose colors are set from code
/// </summary>
public Color Blue { get; private set; } = Color.Blue;
public readonly static GUIColor Blue = new GUIColor("Blue");
/// <summary>
/// General yellow color used for elements whose colors are set from code
/// </summary>
public Color Yellow { get; private set; } = Color.Yellow;
public readonly static GUIColor Yellow = new GUIColor("Yellow");
public Color ColorInventoryEmpty { get; private set; } = Color.Red;
public Color ColorInventoryHalf { get; private set; } = Color.Orange;
public Color ColorInventoryFull { get; private set; } = Color.LightGreen;
public Color ColorInventoryBackground { get; private set; } = Color.Gray;
public Color ColorInventoryEmptyOverlay { get; private set; } = Color.Red;
/// <summary>
/// Color to display the name of modded servers in the server list.
/// </summary>
public readonly static GUIColor ModdedServerColor = new GUIColor("ModdedServerColor");
public Color TextColor { get; private set; } = Color.White * 0.8f;
public Color TextColorBright { get; private set; } = Color.White * 0.9f;
public Color TextColorDark { get; private set; } = Color.Black * 0.9f;
public Color TextColorDim { get; private set; } = Color.White * 0.6f;
public readonly static GUIColor ColorInventoryEmpty = new GUIColor("ColorInventoryEmpty");
public readonly static GUIColor ColorInventoryHalf = new GUIColor("ColorInventoryHalf");
public readonly static GUIColor ColorInventoryFull = new GUIColor("ColorInventoryFull");
public readonly static GUIColor ColorInventoryBackground = new GUIColor("ColorInventoryBackground");
public readonly static GUIColor ColorInventoryEmptyOverlay = new GUIColor("ColorInventoryEmptyOverlay");
public Color ItemQualityColorPoor { get; private set; } = Color.DarkRed;
public Color ItemQualityColorNormal { get; private set; } = Color.Gray;
public Color ItemQualityColorGood { get; private set; } = Color.LightGreen;
public Color ItemQualityColorExcellent { get; private set; } = Color.LightBlue;
public Color ItemQualityColorMasterwork { get; private set; } = Color.MediumPurple;
public readonly static GUIColor TextColorNormal = new GUIColor("TextColorNormal");
public readonly static GUIColor TextColorBright = new GUIColor("TextColorBright");
public readonly static GUIColor TextColorDark = new GUIColor("TextColorDark");
public readonly static GUIColor TextColorDim = new GUIColor("TextColorDim");
public Color ColorReputationVeryLow { get; private set; } = Color.Red;
public Color ColorReputationLow { get; private set; } = Color.Orange;
public Color ColorReputationNeutral { get; private set; } = Color.White * 0.8f;
public Color ColorReputationHigh { get; private set; } = Color.LightBlue;
public Color ColorReputationVeryHigh { get; private set; } = Color.Blue;
public readonly static GUIColor ItemQualityColorPoor = new GUIColor("ItemQualityColorPoor");
public readonly static GUIColor ItemQualityColorNormal = new GUIColor("ItemQualityColorNormal");
public readonly static GUIColor ItemQualityColorGood = new GUIColor("ItemQualityColorGood");
public readonly static GUIColor ItemQualityColorExcellent = new GUIColor("ItemQualityColorExcellent");
public readonly static GUIColor ItemQualityColorMasterwork = new GUIColor("ItemQualityColorMasterwork");
public readonly static GUIColor ColorReputationVeryLow = new GUIColor("ColorReputationVeryLow");
public readonly static GUIColor ColorReputationLow = new GUIColor("ColorReputationLow");
public readonly static GUIColor ColorReputationNeutral = new GUIColor("ColorReputationNeutral");
public readonly static GUIColor ColorReputationHigh = new GUIColor("ColorReputationHigh");
public readonly static GUIColor ColorReputationVeryHigh = new GUIColor("ColorReputationVeryHigh");
// Inventory
public Color EquipmentSlotIconColor { get; private set; } = new Color(99, 70, 64);
public readonly static GUIColor EquipmentSlotIconColor = new GUIColor("EquipmentSlotIconColor");
// Health HUD
public Color BuffColorLow { get; private set; } = Color.LightGreen;
public Color BuffColorMedium { get; private set; } = Color.Green;
public Color BuffColorHigh { get; private set; } = Color.DarkGreen;
public readonly static GUIColor BuffColorLow = new GUIColor("BuffColorLow");
public readonly static GUIColor BuffColorMedium = new GUIColor("BuffColorMedium");
public readonly static GUIColor BuffColorHigh = new GUIColor("BuffColorHigh");
public Color DebuffColorLow { get; private set; } = Color.DarkSalmon;
public Color DebuffColorMedium { get; private set; } = Color.Red;
public Color DebuffColorHigh { get; private set; } = Color.DarkRed;
public readonly static GUIColor DebuffColorLow = new GUIColor("DebuffColorLow");
public readonly static GUIColor DebuffColorMedium = new GUIColor("DebuffColorMedium");
public readonly static GUIColor DebuffColorHigh = new GUIColor("DebuffColorHigh");
public Color HealthBarColorLow { get; private set; } = Color.Red;
public Color HealthBarColorMedium { get; private set; } = Color.Orange;
public Color HealthBarColorHigh { get; private set; } = new Color(78, 114, 88);
public readonly static GUIColor HealthBarColorLow = new GUIColor("HealthBarColorLow");
public readonly static GUIColor HealthBarColorMedium = new GUIColor("HealthBarColorMedium");
public readonly static GUIColor HealthBarColorHigh = new GUIColor("HealthBarColorHigh");
public Color EquipmentIndicatorNotEquipped { get; private set; } = Color.Gray;
public Color EquipmentIndicatorEquipped { get; private set; } = new Color(105, 202, 125);
public Color EquipmentIndicatorRunningOut { get; private set; } = new Color(202, 105, 105);
public readonly static GUIColor EquipmentIndicatorNotEquipped = new GUIColor("EquipmentIndicatorNotEquipped");
public readonly static GUIColor EquipmentIndicatorEquipped = new GUIColor("EquipmentIndicatorEquipped");
public readonly static GUIColor EquipmentIndicatorRunningOut = new GUIColor("EquipmentIndicatorRunningOut");
public static Point ItemFrameMargin => new Point(50, 56).Multiply(GUI.SlicedSpriteScale);
public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
public GUIStyle(XElement element, GraphicsDevice graphicsDevice)
public static GUIComponentStyle GetComponentStyle(string name)
=> ComponentStyles.ContainsKey(name) ? ComponentStyles[name] : null;
public static void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null)
{
this.graphicsDevice = graphicsDevice;
componentStyles = new Dictionary<string, GUIComponentStyle>();
configElement = element;
foreach (XElement subElement in configElement.Elements())
{
var name = subElement.Name.ToString().ToLowerInvariant();
switch (name)
{
case "cursor":
if (subElement.HasElements)
{
foreach (var children in subElement.Descendants())
{
var index = children.GetAttributeInt("state", (int)CursorState.Default);
CursorSprite[index] = new Sprite(children);
}
}
else
{
CursorSprite[(int)CursorState.Default] = new Sprite(subElement);
}
break;
case "green":
Green = subElement.GetAttributeColor("color", Green);
break;
case "orange":
Orange = subElement.GetAttributeColor("color", Orange);
break;
case "red":
Red = subElement.GetAttributeColor("color", Red);
break;
case "blue":
Blue = subElement.GetAttributeColor("color", Blue);
break;
case "yellow":
Yellow = subElement.GetAttributeColor("color", Yellow);
break;
case "colorinventoryempty":
ColorInventoryEmpty = subElement.GetAttributeColor("color", ColorInventoryEmpty);
break;
case "colorinventoryhalf":
ColorInventoryHalf = subElement.GetAttributeColor("color", ColorInventoryHalf);
break;
case "colorinventoryfull":
ColorInventoryFull = subElement.GetAttributeColor("color", ColorInventoryFull);
break;
case "colorinventorybackground":
ColorInventoryBackground = subElement.GetAttributeColor("color", ColorInventoryBackground);
break;
case "colorinventoryemptyoverlay":
ColorInventoryEmptyOverlay = subElement.GetAttributeColor("color", ColorInventoryEmptyOverlay);
break;
case "textcolordark":
TextColorDark = subElement.GetAttributeColor("color", TextColorDark);
break;
case "textcolorbright":
TextColorBright = subElement.GetAttributeColor("color", TextColorBright);
break;
case "textcolordim":
TextColorDim = subElement.GetAttributeColor("color", TextColorDim);
break;
case "textcolornormal":
case "textcolor":
TextColor = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationverylow":
ColorReputationVeryLow = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationlow":
ColorReputationLow = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationneutral":
ColorReputationNeutral = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationhigh":
ColorReputationHigh = subElement.GetAttributeColor("color", TextColor);
break;
case "colorreputationveryhigh":
ColorReputationVeryHigh = subElement.GetAttributeColor("color", TextColor);
break;
case "equipmentsloticoncolor":
EquipmentSlotIconColor = subElement.GetAttributeColor("color", EquipmentSlotIconColor);
break;
case "buffcolorlow":
BuffColorLow = subElement.GetAttributeColor("color", BuffColorLow);
break;
case "buffcolormedium":
BuffColorMedium = subElement.GetAttributeColor("color", BuffColorMedium);
break;
case "buffcolorhigh":
BuffColorHigh = subElement.GetAttributeColor("color", BuffColorHigh);
break;
case "debuffcolorlow":
DebuffColorLow = subElement.GetAttributeColor("color", DebuffColorLow);
break;
case "debuffcolormedium":
DebuffColorMedium = subElement.GetAttributeColor("color", DebuffColorMedium);
break;
case "debuffcolorhigh":
DebuffColorHigh = subElement.GetAttributeColor("color", DebuffColorHigh);
break;
case "healthbarcolorlow":
HealthBarColorLow = subElement.GetAttributeColor("color", HealthBarColorLow);
break;
case "healthbarcolormedium":
HealthBarColorMedium = subElement.GetAttributeColor("color", HealthBarColorMedium);
break;
case "healthbarcolorhigh":
HealthBarColorHigh = subElement.GetAttributeColor("color", HealthBarColorHigh);
break;
case "equipmentindicatornotequipped":
EquipmentIndicatorNotEquipped = subElement.GetAttributeColor("color", EquipmentIndicatorNotEquipped);
break;
case "equipmentindicatorequipped":
EquipmentIndicatorEquipped = subElement.GetAttributeColor("color", EquipmentIndicatorEquipped);
break;
case "equipmentindicatorrunningout":
EquipmentIndicatorRunningOut = subElement.GetAttributeColor("color", EquipmentIndicatorRunningOut);
break;
case "uiglow":
UIGlow = new UISprite(subElement);
break;
case "pingcircle":
PingCircle = new UISprite(subElement);
break;
case "youareherecircle":
YouAreHereCircle = new UISprite(subElement);
break;
case "radiation":
RadiationSprite = new UISprite(subElement);
break;
case "radiationanimspritesheet":
RadiationAnimSpriteSheet = new SpriteSheet(subElement);
break;
case "uiglowcircular":
UIGlowCircular = new UISprite(subElement);
break;
case "uiglowsolidcircular":
UIGlowSolidCircular = new UISprite(subElement);
break;
case "uithermalglow":
UIThermalGlow = new UISprite(subElement);
break;
case "endroundbuttonpulse":
ButtonPulse = new UISprite(subElement);
break;
case "iconoverflowindicator":
IconOverflowIndicator = new UISprite(subElement);
break;
case "focusindicator":
FocusIndicator = new SpriteSheet(subElement);
break;
case "savingindicator":
SavingIndicator = new SpriteSheet(subElement);
break;
case "font":
Font = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[Font] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "globalfont":
GlobalFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[GlobalFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "unscaledsmallfont":
UnscaledSmallFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[UnscaledSmallFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "smallfont":
SmallFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[SmallFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "largefont":
LargeFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[LargeFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "digitalfont":
DigitalFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[DigitalFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "monospacedfont":
MonospacedFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[MonospacedFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "hotkeyfont":
HotkeyFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[HotkeyFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "objectivetitle":
case "subheading":
SubHeadingFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[SubHeadingFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
default:
GUIComponentStyle componentStyle = new GUIComponentStyle(subElement, this);
componentStyles.Add(subElement.Name.ToString().ToLowerInvariant(), componentStyle);
break;
}
}
if (GlobalFont == null)
{
GlobalFont = Font;
DebugConsole.NewMessage("Global font not defined in the current UI style file. The global font is used to render western symbols when using Chinese/Japanese/Korean localization. Using default font instead...", Color.Orange);
}
// TODO: Needs to unregister if we ever remove GUIStyles.
GameMain.Instance.ResolutionChanged += RescaleElements;
Apply(targetComponent, styleName.ToIdentifier(), parent);
}
/// <summary>
/// Returns the default font of the currently selected language
/// </summary>
public ScalableFont LoadCurrentDefaultFont()
{
defaultFont?.Dispose();
defaultFont = null;
foreach (XElement subElement in configElement.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "font":
defaultFont = LoadFont(subElement, graphicsDevice);
break;
}
}
return defaultFont;
}
private void RescaleElements()
{
if (configElement == null) { return; }
if (configElement.Elements() == null) { return; }
foreach (XElement subElement in configElement.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "font":
if (Font == null) { continue; }
Font.Size = GetFontSize(subElement);
break;
case "smallfont":
if (SmallFont == null) { continue; }
SmallFont.Size = GetFontSize(subElement);
break;
case "largefont":
if (LargeFont == null) { continue; }
LargeFont.Size = GetFontSize(subElement);
break;
case "hotkeyfont":
if (HotkeyFont == null) { continue; }
HotkeyFont.Size = GetFontSize(subElement);
break;
case "objectivetitle":
case "subheading":
if (SubHeadingFont == null) { continue; }
SubHeadingFont.Size = GetFontSize(subElement);
break;
}
}
foreach (var componentStyle in componentStyles.Values)
{
componentStyle.GetSize(componentStyle.Element);
foreach (var childStyle in componentStyle.ChildStyles.Values)
{
childStyle.GetSize(childStyle.Element);
}
}
}
private ScalableFont LoadFont(XElement element, GraphicsDevice graphicsDevice)
{
string file = GetFontFilePath(element);
uint size = GetFontSize(element);
bool dynamicLoading = GetFontDynamicLoading(element);
bool isCJK = GetIsCJK(element);
return new ScalableFont(file, size, graphicsDevice, dynamicLoading, isCJK);
}
private uint GetFontSize(XElement element, uint defaultSize = 14)
{
//check if any of the language override fonts want to override the font size as well
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
{
uint overrideFontSize = GetFontSize(subElement, 0);
if (overrideFontSize > 0) { return (uint)Math.Round(overrideFontSize * GameSettings.TextScale); }
}
}
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("size", StringComparison.OrdinalIgnoreCase)) { continue; }
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
{
return (uint)Math.Round(subElement.GetAttributeInt("size", 14) * GameSettings.TextScale);
}
}
return (uint)Math.Round(defaultSize * GameSettings.TextScale);
}
private string GetFontFilePath(XElement element)
{
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
{
return subElement.GetAttributeString("file", "");
}
}
return element.GetAttributeString("file", "");
}
private bool GetFontDynamicLoading(XElement element)
{
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
{
return subElement.GetAttributeBool("dynamicloading", false);
}
}
return element.GetAttributeBool("dynamicloading", false);
}
private bool GetIsCJK(XElement element)
{
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
{
return subElement.GetAttributeBool("iscjk", false);
}
}
return element.GetAttributeBool("iscjk", false);
}
public GUIComponentStyle GetComponentStyle(string name)
{
componentStyles.TryGetValue(name.ToLowerInvariant(), out GUIComponentStyle style);
return style;
}
public void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null)
public static void Apply(GUIComponent targetComponent, Identifier styleName, GUIComponent parent = null)
{
GUIComponentStyle componentStyle = null;
if (parent != null)
{
GUIComponentStyle parentStyle = parent.Style;
if (parent.Style == null)
if (parentStyle == null)
{
string parentStyleName = parent.GetType().Name.ToLowerInvariant();
Identifier parentStyleName = parent.GetType().Name.ToIdentifier();
if (!componentStyles.TryGetValue(parentStyleName, out parentStyle))
if (!ComponentStyles.ContainsKey(parentStyleName))
{
DebugConsole.ThrowError("Couldn't find a GUI style \""+ parentStyleName + "\"");
DebugConsole.ThrowError($"Couldn't find a GUI style \"{parentStyleName}\"");
return;
}
parentStyle = ComponentStyles[parentStyleName];
}
string childStyleName = string.IsNullOrEmpty(styleName) ? targetComponent.GetType().Name : styleName;
parentStyle.ChildStyles.TryGetValue(childStyleName.ToLowerInvariant(), out componentStyle);
Identifier childStyleName = styleName.IsEmpty ? targetComponent.GetType().Name.ToIdentifier() : styleName;
parentStyle.ChildStyles.TryGetValue(childStyleName, out componentStyle);
}
else
{
if (string.IsNullOrEmpty(styleName))
Identifier styleIdentifier = styleName.ToIdentifier();
if (styleIdentifier == Identifier.Empty)
{
styleName = targetComponent.GetType().Name;
styleIdentifier = targetComponent.GetType().Name.ToIdentifier();
}
if (!componentStyles.TryGetValue(styleName.ToLowerInvariant(), out componentStyle))
if (!ComponentStyles.ContainsKey(styleIdentifier))
{
DebugConsole.ThrowError("Couldn't find a GUI style \""+ styleName+"\"");
DebugConsole.ThrowError($"Couldn't find a GUI style \"{styleIdentifier}\"");
return;
}
componentStyle = ComponentStyles[styleIdentifier];
}
targetComponent.ApplyStyle(componentStyle);
}
public Color GetQualityColor(int quality)
public static GUIColor GetQualityColor(int quality)
{
switch (quality)
{

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
@@ -7,9 +8,16 @@ using System.Linq;
namespace Barotrauma
{
public enum ForceUpperCase
{
Inherit,
No,
Yes
}
public class GUITextBlock : GUIComponent
{
protected string text;
protected RichString text;
protected Alignment textAlignment;
@@ -20,10 +28,10 @@ namespace Barotrauma
protected Color textColor, disabledTextColor, selectedTextColor;
private string wrappedText;
private LocalizedString wrappedText;
private string censoredText;
public delegate string TextGetterHandler();
public delegate LocalizedString TextGetterHandler();
public TextGetterHandler TextGetter;
public bool Wrap;
@@ -41,8 +49,6 @@ namespace Barotrauma
private float textDepth;
private ScalableFont originalFont;
public Vector2 TextOffset { get; set; }
private Vector4 padding;
@@ -56,7 +62,7 @@ namespace Barotrauma
}
}
public override ScalableFont Font
public override GUIFont Font
{
get
{
@@ -65,23 +71,25 @@ namespace Barotrauma
set
{
if (base.Font == value) { return; }
base.Font = originalFont = value;
if (text != null && GUI.Style.ForceFontUpperCase.ContainsKey(Font) && GUI.Style.ForceFontUpperCase[Font])
{
Text = text.ToUpper();
}
base.Font = value;
if (text != null) { Text = text; }
SetTextPos();
}
}
public string Text
public RichString Text
{
get { return text; }
set
{
string newText = forceUpperCase || (GUI.Style.ForceFontUpperCase.ContainsKey(Font) && GUI.Style.ForceFontUpperCase[Font]) || (style != null && style.ForceUpperCase) ?
value?.ToUpper() :
value;
#warning TODO: Remove this eventually. Nobody should want to pass null.
value ??= "";
RichString newText = forceUpperCase switch
{
ForceUpperCase.Inherit => value.CaseTiedToFontAndStyle(Font, Style),
ForceUpperCase.No => value.CaseTiedToFontAndStyle(null, null),
ForceUpperCase.Yes => value.ToUpper()
};
if (Text == newText) { return; }
@@ -89,21 +97,12 @@ namespace Barotrauma
if (autoScaleHorizontal || autoScaleVertical) { textScale = 1.0f; }
text = newText;
wrappedText = newText;
if (TextManager.IsCJK(text))
{
//switch to fallback CJK font
if (!Font.IsCJK) { base.Font = GUI.CJKFont; }
}
else
{
if (Font == GUI.CJKFont) { base.Font = originalFont; }
}
wrappedText = newText.SanitizedString;
SetTextPos();
}
}
public string WrappedText
public LocalizedString WrappedText
{
get { return wrappedText; }
}
@@ -117,7 +116,11 @@ namespace Barotrauma
public Vector2 TextPos
{
get { return textPos; }
set { textPos = value; }
set
{
textPos = value;
ClearCaretPositions();
}
}
public float TextScale
@@ -169,8 +172,8 @@ namespace Barotrauma
}
}
private bool forceUpperCase;
public bool ForceUpperCase
private ForceUpperCase forceUpperCase = ForceUpperCase.Inherit;
public ForceUpperCase ForceUpperCase
{
get { return forceUpperCase; }
set
@@ -178,12 +181,7 @@ namespace Barotrauma
if (forceUpperCase == value) { return; }
forceUpperCase = value;
if (forceUpperCase ||
(style != null && style.ForceUpperCase) ||
(GUI.Style.ForceFontUpperCase.ContainsKey(Font) && GUI.Style.ForceFontUpperCase[Font]))
{
Text = text?.ToUpper();
}
if (text != null) { Text = text; }
}
}
@@ -247,7 +245,7 @@ namespace Barotrauma
public class StrikethroughSettings
{
public Color Color { get; set; } = GUI.Style.Red;
public Color Color { get; set; } = GUIStyle.Red;
private int thickness;
private int expand;
@@ -266,13 +264,9 @@ namespace Barotrauma
public StrikethroughSettings Strikethrough = null;
public List<RichTextData> RichTextData
{
get;
private set;
}
public ImmutableArray<RichTextData>? RichTextData => text.RichTextData;
public bool HasColorHighlight => RichTextData != null;
public bool HasColorHighlight => RichTextData.HasValue;
public bool OverrideRichTextDataAlpha = true;
@@ -292,9 +286,9 @@ namespace Barotrauma
/// This is the new constructor.
/// If the rectT height is set 0, the height is calculated from the text.
/// </summary>
public GUITextBlock(RectTransform rectT, string text, Color? textColor = null, ScalableFont font = null,
public GUITextBlock(RectTransform rectT, RichString text, Color? textColor = null, GUIFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null,
bool playerInput = false, bool parseRichText = false)
bool playerInput = false)
: base(style, rectT)
{
if (color.HasValue)
@@ -306,28 +300,15 @@ namespace Barotrauma
OverrideTextColor(textColor.Value);
}
if (parseRichText)
{
RichTextData = Barotrauma.RichTextData.GetRichTextData(text, out text);
if (RichTextData != null && RichTextData.Count == 0)
{
RichTextData = null;
}
}
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
//use the default CJK font as a fallback
var selectedFont = originalFont = font ?? GUI.Font;
if (TextManager.IsCJK(text) && !selectedFont.IsCJK)
{
selectedFont = GUI.CJKFont;
}
var selectedFont = font ?? GUIStyle.Font;
this.Font = selectedFont;
this.textAlignment = textAlignment;
this.Wrap = wrap;
this.Text = text ?? "";
this.playerInput = playerInput;
if (rectT.Rect.Height == 0 && !string.IsNullOrEmpty(text))
if (rectT.Rect.Height == 0 && !text.IsNullOrEmpty())
{
CalculateHeightFromText();
}
@@ -339,11 +320,6 @@ namespace Barotrauma
Enabled = true;
Censor = false;
}
public GUITextBlock(RectTransform rectT, List<RichTextData> richTextData, string text, Color? textColor = null, ScalableFont font = null, Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false)
: this(rectT, text, textColor, font, textAlignment, wrap, style, color, playerInput)
{
this.RichTextData = richTextData;
}
public void CalculateHeightFromText(int padding = 0, bool removeExtraSpacing = false)
{
@@ -351,10 +327,9 @@ namespace Barotrauma
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(wrappedText, removeExtraSpacing).Y + padding));
}
public void SetRichText(string richText)
public void SetRichText(LocalizedString richText)
{
RichTextData = Barotrauma.RichTextData.GetRichTextData(richText, out string sanitizedText);
Text = sanitizedText;
Text = RichString.Rich(richText);
}
public override void ApplyStyle(GUIComponentStyle componentStyle)
@@ -368,41 +343,34 @@ namespace Barotrauma
disabledTextColor = componentStyle.DisabledTextColor;
selectedTextColor = componentStyle.SelectedTextColor;
switch (componentStyle.Font)
if (Font == null || !componentStyle.Font.IsEmpty)
{
case "font":
Font = componentStyle.Style.Font;
break;
case "smallfont":
Font = componentStyle.Style.SmallFont;
break;
case "largefont":
Font = componentStyle.Style.LargeFont;
break;
case "objectivetitle":
case "subheading":
Font = componentStyle.Style.SubHeadingFont;
break;
Font = GUIStyle.Fonts[componentStyle.Font.AppendIfMissing("Font")];
}
}
public void ClearCaretPositions()
{
cachedCaretPositions = ImmutableArray<Vector2>.Empty;
}
public void SetTextPos()
{
cachedCaretPositions = ImmutableArray<Vector2>.Empty;
ClearCaretPositions();
if (text == null) { return; }
censoredText = string.IsNullOrEmpty(text) ? "" : new string('\u2022', text.Length);
censoredText = text.IsNullOrEmpty() ? "" : new string('\u2022', text.Length);
var rect = Rect;
overflowClipActive = false;
wrappedText = text;
wrappedText = text.SanitizedString;
TextSize = MeasureText(text);
TextSize = MeasureText(text.SanitizedString);
if (Wrap && rect.Width > 0)
{
wrappedText = ToolBox.WrapText(text, rect.Width - padding.X - padding.Z, Font, textScale);
wrappedText = ToolBox.WrapText(text.SanitizedString, rect.Width - padding.X - padding.Z, Font, textScale);
TextSize = MeasureText(wrappedText);
}
else if (OverflowClip)
@@ -426,15 +394,15 @@ namespace Barotrauma
textPos = new Vector2(padding.X + (rect.Width - padding.Z - padding.X) / 2.0f, padding.Y + (rect.Height - padding.Y - padding.W) / 2.0f);
origin = TextSize * 0.5f;
origin.X = 0;
if (textAlignment.HasFlag(Alignment.Left) && !overflowClipActive)
{
textPos.X = padding.X;
origin.X = 0;
}
if (textAlignment.HasFlag(Alignment.Right) || overflowClipActive)
{
textPos.X = rect.Width - padding.Z;
origin.X = TextSize.X;
//origin.X = TextSize.X;
}
if (textAlignment.HasFlag(Alignment.Top))
{
@@ -454,7 +422,12 @@ namespace Barotrauma
textPos.Y = (int)textPos.Y;
}
private Vector2 MeasureText(string text)
private Vector2 MeasureText(LocalizedString text)
{
return MeasureText(text.Value);
}
private Vector2 MeasureText(string text)
{
if (Font == null) return Vector2.Zero;
@@ -498,12 +471,20 @@ namespace Barotrauma
{
return cachedCaretPositions;
}
string textDrawn = Censor ? CensoredText : Text;
string textDrawn = Censor ? CensoredText : Text.SanitizedValue;
float w = Wrap
? (Rect.Width - Padding.X - Padding.Z) / TextScale
: float.PositiveInfinity;
Font.WrapText(textDrawn, w, out Vector2[] positions);
cachedCaretPositions = positions.Select(p => p * TextScale + TextPos - Origin * TextScale).ToImmutableArray();
string wrapped = Font.WrapText(textDrawn, w, out Vector2[] positions);
int textWidth = (int)Font.MeasureString(wrapped).X;
int alignmentXDiff
= textAlignment.HasFlag(Alignment.Right) ? textWidth
: textAlignment.HasFlag(Alignment.Center) ? textWidth / 2
: 0;
cachedCaretPositions = positions
.Select(p => p - new Vector2(alignmentXDiff, 0))
.Select(p => p * TextScale + TextPos - Origin * TextScale)
.ToImmutableArray();
return cachedCaretPositions;
}
@@ -584,7 +565,7 @@ namespace Barotrauma
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
}
if (!string.IsNullOrEmpty(text))
if (!text.IsNullOrEmpty())
{
Vector2 pos = rect.Location.ToVector2() + textPos + TextOffset;
if (RoundToNearestPixel)
@@ -605,28 +586,29 @@ namespace Barotrauma
if (!HasColorHighlight)
{
string textToShow = Censor ? censoredText : (Wrap ? wrappedText : text);
string textToShow = Censor ? censoredText : (Wrap ? wrappedText.Value : text.SanitizedValue);
Color colorToShow = currentTextColor * (currentTextColor.A / 255.0f);
if (Shadow)
{
Vector2 shadowOffset = new Vector2(GUI.IntScale(2));
Font.DrawString(spriteBatch, textToShow, pos + shadowOffset, Color.Black, 0.0f, origin, TextScale, SpriteEffects.None, textDepth);
Font.DrawString(spriteBatch, textToShow, pos + shadowOffset, Color.Black, 0.0f, origin, TextScale, SpriteEffects.None, textDepth, alignment: textAlignment, forceUpperCase: ForceUpperCase);
}
Font.DrawString(spriteBatch, textToShow, pos, colorToShow, 0.0f, origin, TextScale, SpriteEffects.None, textDepth);
Font.DrawString(spriteBatch, textToShow, pos, colorToShow, 0.0f, origin, TextScale, SpriteEffects.None, textDepth, alignment: textAlignment, forceUpperCase: ForceUpperCase);
}
else
{
if (OverrideRichTextDataAlpha)
{
RichTextData.ForEach(rt => rt.Alpha = currentTextColor.A / 255.0f);
RichTextData.Value.ForEach(rt => rt.Alpha = currentTextColor.A / 255.0f);
}
Font.DrawStringWithColors(spriteBatch, Censor ? censoredText : (Wrap ? wrappedText : text), pos,
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData);
Font.DrawStringWithColors(spriteBatch, Censor ? censoredText : (Wrap ? wrappedText : text.SanitizedString).Value, pos,
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData.Value, alignment: textAlignment, forceUpperCase: ForceUpperCase);
}
Strikethrough?.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X, ForceUpperCase ? pos.Y : pos.Y + GUI.Scale * 2f);
Strikethrough?.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X,
/* TODO: ???? */ForceUpperCase == ForceUpperCase.Yes ? pos.Y : pos.Y + GUI.Scale * 2f);
}
if (overflowClipActive)

View File

@@ -76,9 +76,6 @@ namespace Barotrauma
private int selectionStartIndex;
private int selectionEndIndex;
private bool IsLeftToRight => selectionStartIndex <= selectionEndIndex;
private Vector2 selectionStartPos;
private Vector2 selectionEndPos;
private Vector2 selectionRectSize;
private GUICustomComponent caretAndSelectionRenderer;
@@ -151,7 +148,7 @@ namespace Barotrauma
maxTextLength = value;
if (Text.Length > MaxTextLength)
{
SetText(textBlock.Text.Substring(0, (int)maxTextLength));
SetText(Text.Substring(0, (int)maxTextLength));
}
}
}
@@ -182,7 +179,7 @@ namespace Barotrauma
set { textBlock.Censor = value; }
}
public override string ToolTip
public override RichString ToolTip
{
get
{
@@ -194,7 +191,7 @@ namespace Barotrauma
}
}
public override ScalableFont Font
public override GUIFont Font
{
get { return textBlock?.Font ?? base.Font; }
set
@@ -247,7 +244,7 @@ namespace Barotrauma
{
get
{
return textBlock.Text;
return textBlock.Text.SanitizedValue;
}
set
{
@@ -259,12 +256,12 @@ namespace Barotrauma
public string WrappedText
{
get { return textBlock.WrappedText; }
get { return textBlock.WrappedText.Value; }
}
public bool Readonly { get; set; }
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, ScalableFont font = null,
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, GUIFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false, bool createPenIcon = true)
: base(style, rectT)
{
@@ -273,9 +270,10 @@ namespace Barotrauma
this.color = color ?? Color.White;
frame = new GUIFrame(new RectTransform(Vector2.One, rectT, Anchor.Center), style, color);
GUI.Style.Apply(frame, style == "" ? "GUITextBox" : style);
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft), text, textColor, font, textAlignment, wrap, playerInput: true);
GUI.Style.Apply(textBlock, "", this);
GUIStyle.Apply(frame, style == "" ? "GUITextBox" : style);
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft), text ?? "", textColor, font, textAlignment, wrap, playerInput: true);
GUIStyle.Apply(textBlock, "", this);
if (font != null) { textBlock.Font = font; }
CaretEnabled = true;
caretPosDirty = true;
@@ -297,10 +295,11 @@ namespace Barotrauma
clearButtonWidth = (int)(clearButton.Rect.Width * 1.2f);
}
if (this.style != null && this.style.ChildStyles.ContainsKey("textboxicon") && createPenIcon)
var selfStyle = Style;
if (selfStyle != null && selfStyle.ChildStyles.ContainsKey("textboxicon".ToIdentifier()) && createPenIcon)
{
icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5 + clearButtonWidth, 0) }, null, scaleToFit: true);
icon.ApplyStyle(this.style.ChildStyles["textboxicon"]);
icon.ApplyStyle(this.Style.ChildStyles["textboxicon".ToIdentifier()]);
textBlock.RectTransform.MaxSize = new Point(frame.Rect.Width - icon.Rect.Height - clearButtonWidth - icon.RectTransform.AbsoluteOffset.X * 2, int.MaxValue);
}
Font = textBlock.Font;
@@ -325,53 +324,39 @@ namespace Barotrauma
{
text = textFilterFunction(text);
}
if (textBlock.Text == text) { return false; }
if (Text == text) { return false; }
textBlock.Text = text;
if (textBlock.Text == null) textBlock.Text = "";
if (textBlock.Text != "" && !Wrap)
ClearSelection();
if (Text == null) textBlock.Text = "";
if (Text != "" && !Wrap)
{
if (maxTextLength != null)
{
if (textBlock.Text.Length > maxTextLength)
{
textBlock.Text = textBlock.Text.Substring(0, (int)maxTextLength);
textBlock.Text = Text.Substring(0, (int)maxTextLength);
}
}
else
{
while (ClampText && textBlock.Text.Length > 0 && Font.MeasureString(textBlock.Text).X * TextBlock.TextScale > (int)(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z))
{
textBlock.Text = textBlock.Text.Substring(0, textBlock.Text.Length - 1);
textBlock.Text = Text.Substring(0, textBlock.Text.Length - 1);
}
}
}
if (store)
{
memento.Store(textBlock.Text);
memento.Store(Text);
}
return true;
}
private void CalculateCaretPos()
{
if (Censor || !Wrap)
{
string textDrawn = textBlock.CensoredText;
CaretIndex = Math.Min(CaretIndex, textDrawn.Length);
textDrawn = Censor ? textBlock.CensoredText : textBlock.Text;
Vector2 textSize = Font.MeasureString(textDrawn[..CaretIndex]) * TextBlock.TextScale;
caretPos = new Vector2(textSize.X, 0) + textBlock.TextPos - textBlock.Origin * TextBlock.TextScale;
}
else
{
CaretIndex = Math.Min(CaretIndex, textBlock.Text.Length);
textBlock.Font.WrapText(
textBlock.Text,
(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z) / TextBlock.TextScale,
CaretIndex,
out Vector2 requestedCharPos);
caretPos = requestedCharPos * TextBlock.TextScale + textBlock.TextPos - textBlock.Origin * TextBlock.TextScale;
}
CaretIndex = Math.Clamp(CaretIndex, 0, textBlock.Text.Length);
var caretPositions = textBlock.GetAllCaretPositions();
caretPos = caretPositions[CaretIndex];
caretPosDirty = false;
}
@@ -470,14 +455,19 @@ namespace Barotrauma
{
if (textBlock.OverflowClipActive)
{
if (CaretScreenPos.X < textBlock.Rect.X + textBlock.Padding.X)
float left = textBlock.Rect.X + textBlock.Padding.X;
if (CaretScreenPos.X < left)
{
textBlock.TextPos = new Vector2(textBlock.TextPos.X + ((textBlock.Rect.X + textBlock.Padding.X) - CaretScreenPos.X), textBlock.TextPos.Y);
float diff = left - CaretScreenPos.X;
textBlock.TextPos = new Vector2(textBlock.TextPos.X + diff, textBlock.TextPos.Y);
CalculateCaretPos();
}
else if (CaretScreenPos.X > textBlock.Rect.Right - textBlock.Padding.Z)
float right = textBlock.Rect.Right - textBlock.Padding.Z;
if (CaretScreenPos.X > right)
{
textBlock.TextPos = new Vector2(textBlock.TextPos.X - (CaretScreenPos.X - (textBlock.Rect.Right - textBlock.Padding.Z)), textBlock.TextPos.Y);
float diff = CaretScreenPos.X - right;
textBlock.TextPos = new Vector2(textBlock.TextPos.X - diff, textBlock.TextPos.Y);
CalculateCaretPos();
}
}
@@ -509,74 +499,57 @@ namespace Barotrauma
private void DrawCaretAndSelection(SpriteBatch spriteBatch, GUICustomComponent customComponent)
{
if (!Visible) { return; }
if (Selected)
if (!Selected) { return; }
if (caretVisible)
{
if (caretVisible )
{
GUI.DrawLine(spriteBatch,
new Vector2(Rect.X + (int)caretPos.X + 2, Rect.Y + caretPos.Y + 3),
new Vector2(Rect.X + (int)caretPos.X + 2, Rect.Y + caretPos.Y + Font.MeasureString("I").Y * textBlock.TextScale - 3),
CaretColor ?? textBlock.TextColor * (textBlock.TextColor.A / 255.0f));
}
if (selectedCharacters > 0)
{
DrawSelectionRect(spriteBatch);
}
//GUI.DrawString(spriteBatch, new Vector2(100, 0), selectedCharacters.ToString(), Color.LightBlue, Color.Black);
//GUI.DrawString(spriteBatch, new Vector2(100, 20), selectionStartIndex.ToString(), Color.White, Color.Black);
//GUI.DrawString(spriteBatch, new Vector2(140, 20), selectionEndIndex.ToString(), Color.White, Color.Black);
//GUI.DrawString(spriteBatch, new Vector2(100, 40), selectedText.ToString(), Color.Yellow, Color.Black);
//GUI.DrawString(spriteBatch, new Vector2(100, 60), $"caret index: {CaretIndex.ToString()}", GUI.Style.Red, Color.Black);
//GUI.DrawString(spriteBatch, new Vector2(100, 80), $"caret pos: {caretPos.ToString()}", GUI.Style.Red, Color.Black);
//GUI.DrawString(spriteBatch, new Vector2(100, 100), $"caret screen pos: {CaretScreenPos.ToString()}", GUI.Style.Red, Color.Black);
//GUI.DrawString(spriteBatch, new Vector2(100, 120), $"text start pos: {(textBlock.TextPos - textBlock.Origin).ToString()}", Color.White, Color.Black);
//GUI.DrawString(spriteBatch, new Vector2(100, 140), $"cursor pos: {PlayerInput.MousePosition.ToString()}", Color.White, Color.Black);
GUI.DrawLine(spriteBatch,
new Vector2(Rect.X + (int)caretPos.X + 2, Rect.Y + caretPos.Y + 3),
new Vector2(Rect.X + (int)caretPos.X + 2, Rect.Y + caretPos.Y + Font.LineHeight * textBlock.TextScale - 3),
CaretColor ?? textBlock.TextColor * (textBlock.TextColor.A / 255.0f));
}
if (selectedCharacters > 0)
{
DrawSelectionRect(spriteBatch);
}
}
private void DrawSelectionRect(SpriteBatch spriteBatch)
{
if (textBlock.WrappedText.Contains("\n"))
{
// Multiline selection
var characterPositions = textBlock.GetAllCaretPositions();
(int startIndex, int endIndex) = selectionStartIndex < selectionEndIndex
? (selectionStartIndex, selectionEndIndex)
: (selectionEndIndex, selectionStartIndex);
endIndex--;
var characterPositions = textBlock.GetAllCaretPositions();
(int startIndex, int endIndex) = IsLeftToRight
? (selectionStartIndex, selectionEndIndex)
: (selectionEndIndex, selectionStartIndex);
endIndex--;
void drawRect(Vector2 topLeft, Vector2 bottomRight)
{
int minWidth = GUI.IntScale(5);
if (bottomRight.X - topLeft.X < minWidth) { bottomRight.X = topLeft.X + minWidth; }
GUI.DrawRectangle(spriteBatch,
Rect.Location.ToVector2() + topLeft,
bottomRight - topLeft,
SelectionColor, isFilled: true);
}
Vector2 topLeft = characterPositions[startIndex];
for (int i = startIndex+1; i <= endIndex; i++)
{
Vector2 currPos = characterPositions[i];
if (!MathUtils.NearlyEqual(topLeft.Y, currPos.Y))
{
Vector2 bottomRight = characterPositions[i - 1];
bottomRight += Font.MeasureChar(Text[i - 1]);
drawRect(topLeft, bottomRight);
topLeft = currPos;
}
}
Vector2 finalBottomRight = characterPositions[endIndex];
finalBottomRight += Font.MeasureChar(Text[endIndex]);
drawRect(topLeft, finalBottomRight);
}
else
void drawRect(Vector2 topLeft, Vector2 bottomRight)
{
// Single line selection
Vector2 topLeft = IsLeftToRight ? selectionStartPos : selectionEndPos;
GUI.DrawRectangle(spriteBatch, Rect.Location.ToVector2() + topLeft, selectionRectSize, SelectionColor, isFilled: true);
int minWidth = GUI.IntScale(5);
if (bottomRight.X - topLeft.X < minWidth) { bottomRight.X = topLeft.X + minWidth; }
GUI.DrawRectangle(spriteBatch,
Rect.Location.ToVector2() + topLeft,
bottomRight - topLeft,
SelectionColor, isFilled: true);
}
Vector2 topLeft = characterPositions[startIndex];
for (int i = startIndex+1; i <= endIndex; i++)
{
Vector2 currPos = characterPositions[i];
if (!MathUtils.NearlyEqual(topLeft.Y, currPos.Y))
{
Vector2 bottomRight = characterPositions[i - 1];
bottomRight += Font.MeasureChar(Text[i - 1]) * TextBlock.TextScale;
drawRect(topLeft, bottomRight);
topLeft = currPos;
}
}
Vector2 finalBottomRight = characterPositions[endIndex];
if (Text.Length > endIndex)
{
finalBottomRight += Font.MeasureChar(Text[endIndex]) * TextBlock.TextScale;
}
drawRect(topLeft, finalBottomRight);
}
public void ReceiveTextInput(char inputChar)
@@ -710,8 +683,8 @@ namespace Barotrauma
float lineHeight = Font.LineHeight * TextBlock.TextScale;
int newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y - lineHeight * 0.5f));
textBlock.Font.WrapText(
textBlock.Text,
(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z) / TextBlock.TextScale,
textBlock.Text.SanitizedValue,
GetWrapWidth(),
newIndex,
out Vector2 requestedCharPos);
requestedCharPos *= TextBlock.TextScale;
@@ -728,8 +701,8 @@ namespace Barotrauma
lineHeight = Font.LineHeight * TextBlock.TextScale;
newIndex = textBlock.GetCaretIndexFromLocalPos(new Vector2(caretPos.X, caretPos.Y + lineHeight * 1.5f));
textBlock.Font.WrapText(
textBlock.Text,
(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z) / TextBlock.TextScale,
textBlock.Text.SanitizedValue,
GetWrapWidth(),
newIndex,
out Vector2 requestedCharPos2);
requestedCharPos2 *= TextBlock.TextScale;
@@ -816,7 +789,6 @@ namespace Barotrauma
{
CaretIndex = 0;
CalculateCaretPos();
selectionStartPos = caretPos;
selectionStartIndex = 0;
CaretIndex = Text.Length;
CalculateSelection();
@@ -847,15 +819,18 @@ namespace Barotrauma
{
if (selectedText.Length == 0) { return; }
selectionStartIndex = Math.Max(0, Math.Min(selectionEndIndex, Math.Min(selectionStartIndex, Text.Length - 1)));
int selectionLength = Math.Min(Text.Length - selectionStartIndex, selectedText.Length);
SetText(Text.Remove(selectionStartIndex, selectionLength));
CaretIndex = Math.Min(Text.Length, selectionStartIndex);
int targetCaretIndex = Math.Max(0, Math.Min(selectionEndIndex, Math.Min(selectionStartIndex, Text.Length - 1)));
int selectionLength = Math.Min(Text.Length - targetCaretIndex, selectedText.Length);
SetText(Text.Remove(targetCaretIndex, selectionLength));
CaretIndex = targetCaretIndex;
ClearSelection();
OnTextChanged?.Invoke(this, Text);
}
private float GetWrapWidth()
=> Wrap ? (textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z) / TextBlock.TextScale : float.PositiveInfinity;
private void InitSelectionStart()
{
if (caretPosDirty)
@@ -865,29 +840,20 @@ namespace Barotrauma
if (selectionStartIndex == -1)
{
selectionStartIndex = CaretIndex;
selectionStartPos = caretPos;
}
}
private void CalculateSelection()
{
string textDrawn = Censor ? textBlock.CensoredText : textBlock.WrappedText;
string textDrawn = Censor ? textBlock.CensoredText : WrappedText;
InitSelectionStart();
selectionEndIndex = Math.Min(CaretIndex, textDrawn.Length);
selectionEndPos = caretPos;
selectedCharacters = Math.Abs(selectionStartIndex - selectionEndIndex);
try
{
if (IsLeftToRight)
{
selectedText = Text.Substring(selectionStartIndex, Math.Min(selectedCharacters, Text.Length));
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionStartIndex, Math.Min(selectedCharacters, textDrawn.Length))) * TextBlock.TextScale;
}
else
{
selectedText = Text.Substring(selectionEndIndex, Math.Min(selectedCharacters, Text.Length));
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionEndIndex, Math.Min(selectedCharacters, textDrawn.Length))) * TextBlock.TextScale;
}
selectedText = Text.Substring(
IsLeftToRight ? selectionStartIndex : selectionEndIndex,
Math.Min(selectedCharacters, Text.Length));
}
catch (ArgumentOutOfRangeException exception)
{

View File

@@ -20,18 +20,18 @@ namespace Barotrauma
public override bool Selected
{
get { return selected; }
get { return isSelected; }
set
{
if (value == selected) { return; }
if (value == isSelected) { return; }
if (radioButtonGroup != null && radioButtonGroup.SelectedRadioButton == this)
{
selected = true;
isSelected = true;
return;
}
selected = value;
State = selected ? ComponentState.Selected : ComponentState.None;
isSelected = value;
State = isSelected ? ComponentState.Selected : ComponentState.None;
if (value && radioButtonGroup != null)
{
radioButtonGroup.SelectRadioButton(this);
@@ -88,7 +88,7 @@ namespace Barotrauma
}
}*/
public override ScalableFont Font
public override GUIFont Font
{
get
{
@@ -112,7 +112,7 @@ namespace Barotrauma
get { return text; }
}
public override string ToolTip
public override RichString ToolTip
{
get { return base.ToolTip; }
set
@@ -123,13 +123,13 @@ namespace Barotrauma
}
}
public string Text
public LocalizedString Text
{
get { return text.Text; }
set { text.Text = value; }
}
public GUITickBox(RectTransform rectT, string label, ScalableFont font = null, string style = "") : base(null, rectT)
public GUITickBox(RectTransform rectT, LocalizedString label, GUIFont font = null, string style = "") : base(null, rectT)
{
CanBeFocused = true;
HoverCursor = CursorState.Hand;
@@ -145,7 +145,7 @@ namespace Barotrauma
SelectedColor = Color.DarkGray,
CanBeFocused = false
};
GUI.Style.Apply(box, style == "" ? "GUITickBox" : style);
GUIStyle.Apply(box, style == "" ? "GUITickBox" : style);
if (box.RectTransform.MinSize.Y > 0)
{
RectTransform.MinSize = box.RectTransform.MinSize;
@@ -159,7 +159,7 @@ namespace Barotrauma
{
CanBeFocused = false
};
GUI.Style.Apply(text, "GUITextBlock", this);
GUIStyle.Apply(text, "GUITextBlock", this);
Enabled = true;
ResizeBox();
@@ -205,13 +205,13 @@ namespace Barotrauma
{
Selected = !Selected;
}
else if (!selected)
else if (!isSelected)
{
Selected = true;
}
}
}
else if (selected)
else if (isSelected)
{
State = ComponentState.Selected;
}

View File

@@ -90,7 +90,8 @@ namespace Barotrauma
if (GameMain.Instance != null)
{
GameMain.Instance.ResolutionChanged += CreateAreas;
GameMain.Config.OnHUDScaleChanged += CreateAreas;
#warning TODO: reimplement
//GameSettings.CurrentConfig.OnHUDScaleChanged += CreateAreas;
CreateAreas();
CharacterInfo.Init();
}
@@ -163,7 +164,7 @@ namespace Barotrauma
public static void Draw(SpriteBatch spriteBatch)
{
GUI.DrawRectangle(spriteBatch, ButtonAreaTop, Color.White * 0.5f);
GUI.DrawRectangle(spriteBatch, MessageAreaTop, GUI.Style.Orange * 0.5f);
GUI.DrawRectangle(spriteBatch, MessageAreaTop, GUIStyle.Orange * 0.5f);
GUI.DrawRectangle(spriteBatch, CrewArea, Color.Blue * 0.5f);
GUI.DrawRectangle(spriteBatch, ChatBoxArea, Color.Cyan * 0.5f);
GUI.DrawRectangle(spriteBatch, HealthBarArea, Color.Red * 0.5f);

View File

@@ -7,6 +7,7 @@ using System.Xml.Linq;
using Barotrauma.Media;
using System.Linq;
using Barotrauma.Extensions;
using System.Collections.Immutable;
namespace Barotrauma
{
@@ -69,14 +70,10 @@ namespace Barotrauma
}
}
private string selectedTip;
private List<RichTextData> selectedTipRichTextData;
private bool selectedTipRichTextUnparsed;
private void SetSelectedTip(string tip)
private RichString selectedTip;
private void SetSelectedTip(LocalizedString tip)
{
selectedTip = tip;
selectedTipRichTextData = null;
selectedTipRichTextUnparsed = true;
selectedTip = RichString.Rich(tip);
}
private readonly object loadMutex = new object();
@@ -113,6 +110,8 @@ namespace Barotrauma
set;
}
public LanguageIdentifier[] AvailableLanguages = null;
public LoadingScreen(GraphicsDevice graphics)
{
defaultBackgroundTexture = TextureLoader.FromFile("Content/Map/LocationPortraits/AlienRuins.png");
@@ -123,12 +122,12 @@ namespace Barotrauma
overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png");
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
DrawLoadingText = true;
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
}
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime)
{
if (GameMain.Config.EnableSplashScreen)
if (GameSettings.CurrentConfig.EnableSplashScreen)
{
try
{
@@ -138,11 +137,11 @@ namespace Barotrauma
catch (Exception e)
{
DebugConsole.ThrowError("Playing splash screen video failed", e);
GameMain.Config.EnableSplashScreen = false;
DisableSplashScreen();
}
}
var titleStyle = GUI.Style?.GetComponentStyle("TitleText");
var titleStyle = GUIStyle.GetComponentStyle("TitleText");
Sprite titleSprite = null;
if (!WaitForLanguageSelection && titleStyle != null && titleStyle.Sprites.ContainsKey(GUIComponent.ComponentState.None))
{
@@ -187,67 +186,58 @@ namespace Barotrauma
}
else if (DrawLoadingText)
{
if (TextManager.Initialized)
LocalizedString loadText;
if (LoadState == 100.0f)
{
string loadText;
if (LoadState == 100.0f)
#if DEBUG
if (GameSettings.CurrentConfig.AutomaticQuickStartEnabled || GameSettings.CurrentConfig.AutomaticCampaignLoadEnabled || (GameSettings.CurrentConfig.TestScreenEnabled && GameMain.FirstLoad))
{
#if DEBUG
if (GameMain.Config.AutomaticQuickStartEnabled || GameMain.Config.AutomaticCampaignLoadEnabled || GameMain.Config.TestScreenEnabled && GameMain.FirstLoad)
{
loadText = "QUICKSTARTING ...";
}
else
{
#endif
loadText = TextManager.Get("PressAnyKey");
#if DEBUG
}
#endif
loadText = "QUICKSTARTING ...";
}
else
{
loadText = TextManager.Get("Loading");
if (LoadState != null)
{
loadText += " " + (int)LoadState + " %";
#endif
loadText = TextManager.Get("PressAnyKey");
#if DEBUG
}
#endif
}
else
{
loadText = TextManager.Get("Loading");
if (LoadState != null)
{
loadText += " " + (int)LoadState + " %";
#if DEBUG
if (GameMain.FirstLoad && GameMain.CancelQuickStart)
{
loadText += " (Quickstart aborted)";
}
#endif
if (GameMain.FirstLoad && GameMain.CancelQuickStart)
{
loadText += " (Quickstart aborted)";
}
}
if (GUI.LargeFont != null)
{
GUI.LargeFont.DrawString(spriteBatch, loadText.ToUpper(),
new Vector2(GameMain.GraphicsWidth / 2.0f - GUI.LargeFont.MeasureString(loadText.ToUpper()).X / 2.0f, GameMain.GraphicsHeight * 0.75f),
Color.White);
#endif
}
}
if (GUI.Font != null && selectedTip != null)
if (GUIStyle.LargeFont.HasValue)
{
if (selectedTipRichTextUnparsed)
{
selectedTipRichTextData = RichTextData.GetRichTextData(selectedTip, out selectedTip);
selectedTipRichTextUnparsed = false;
}
GUIStyle.LargeFont.DrawString(spriteBatch, loadText.ToUpper(),
new Vector2(GameMain.GraphicsWidth / 2.0f - GUIStyle.LargeFont.MeasureString(loadText.ToUpper()).X / 2.0f, GameMain.GraphicsHeight * 0.75f),
Color.White);
}
string wrappedTip = ToolBox.WrapText(selectedTip, GameMain.GraphicsWidth * 0.5f, GUI.Font);
if (GUIStyle.Font.HasValue && selectedTip != null)
{
string wrappedTip = ToolBox.WrapText(selectedTip.SanitizedValue, GameMain.GraphicsWidth * 0.5f, GUIStyle.Font.Value);
string[] lines = wrappedTip.Split('\n');
float lineHeight = GUI.Font.MeasureString(selectedTip).Y;
float lineHeight = GUIStyle.Font.MeasureString(selectedTip).Y;
if (selectedTipRichTextData != null)
if (selectedTip.RichTextData != null)
{
int rtdOffset = 0;
for (int i = 0; i < lines.Length; i++)
{
GUI.Font.DrawStringWithColors(spriteBatch, lines[i],
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White,
0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTipRichTextData, rtdOffset);
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,
0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTip.RichTextData.Value, rtdOffset);
rtdOffset += lines[i].Length;
}
}
@@ -255,8 +245,8 @@ namespace Barotrauma
{
for (int i = 0; i < lines.Length; i++)
{
GUI.Font.DrawString(spriteBatch, lines[i],
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
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);
}
}
}
@@ -280,7 +270,7 @@ namespace Barotrauma
if (noiseVal < 0.2f)
{
//SCP-CB reference
randText = (new string[] { "NIL", "black white gray", "Sometimes we would have had time to scream", "e8m106]af", "NO" }).GetRandom();
randText = (new string[] { "NIL", "black white gray", "Sometimes we would have had time to scream", "e8m106]af", "NO" }).GetRandomUnsynced();
}
else if (noiseVal < 0.3f)
{
@@ -295,15 +285,20 @@ namespace Barotrauma
Rand.Int(100).ToString().PadLeft(2, '0');
}
GUI.LargeFont?.DrawString(spriteBatch, randText,
new Vector2(GameMain.GraphicsWidth - decorativeMap.FrameSize.X * decorativeScale.X * 0.8f, GameMain.GraphicsHeight * 0.57f),
Color.White * (1.0f - noiseVal));
if (GUIStyle.LargeFont.HasValue)
{
GUIStyle.LargeFont.DrawString(spriteBatch, randText,
new Vector2(GameMain.GraphicsWidth - decorativeMap.FrameSize.X * decorativeScale.X * 0.8f, GameMain.GraphicsHeight * 0.57f),
Color.White * (1.0f - noiseVal));
}
spriteBatch.End();
}
private void DrawLanguageSelectionPrompt(SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
if (AvailableLanguages is null) { return; }
if (languageSelectionFont == null)
{
languageSelectionFont = new ScalableFont("Content/Fonts/NotoSans/NotoSans-Bold.ttf",
@@ -320,8 +315,8 @@ namespace Barotrauma
}
Vector2 textPos = new Vector2(GameMain.GraphicsWidth / 2, GameMain.GraphicsHeight * 0.3f);
Vector2 textSpacing = new Vector2(0.0f, (GameMain.GraphicsHeight * 0.5f) / TextManager.AvailableLanguages.Count());
foreach (string language in TextManager.AvailableLanguages)
Vector2 textSpacing = new Vector2(0.0f, (GameMain.GraphicsHeight * 0.5f) / AvailableLanguages.Length);
foreach (LanguageIdentifier language in AvailableLanguages)
{
string localizedLanguageName = TextManager.GetTranslatedLanguageName(language);
var font = TextManager.IsCJK(localizedLanguageName) ? languageSelectionFontCJK : languageSelectionFont;
@@ -335,11 +330,11 @@ namespace Barotrauma
hover ? Color.White : Color.White * 0.6f);
if (hover && PlayerInput.PrimaryMouseButtonClicked())
{
GameMain.Config.Language = language;
var config = GameSettings.CurrentConfig;
config.Language = language;
GameSettings.SetCurrentConfig(config);
//reload tip in the selected language
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
GameMain.Config.SetDefaultBindings(legacy: false);
GameMain.Config.CheckBindings(useDefaults: true);
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
WaitForLanguageSelection = false;
languageSelectionFont?.Dispose(); languageSelectionFont = null;
languageSelectionFontCJK?.Dispose(); languageSelectionFontCJK = null;
@@ -368,7 +363,7 @@ namespace Barotrauma
}
catch (Exception e)
{
GameMain.Config.EnableSplashScreen = false;
DisableSplashScreen();
DebugConsole.ThrowError("Playing the splash screen \"" + fileName + "\" failed.", e);
PendingSplashScreens.Clear();
currSplashScreen = null;
@@ -425,13 +420,20 @@ namespace Barotrauma
}
}
private void DisableSplashScreen()
{
var config = GameSettings.CurrentConfig;
config.EnableSplashScreen = false;
GameSettings.SetCurrentConfig(config);
}
bool drawn;
public IEnumerable<CoroutineStatus> DoLoading(IEnumerable<CoroutineStatus> loader)
{
drawn = false;
LoadState = null;
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
currentBackgroundTexture = LocationType.Prefabs.GetRandomUnsynced()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
while (!drawn)
{

View File

@@ -50,7 +50,7 @@ namespace Barotrauma
Afflictions = new List<PendingAfflictionElement>();
}
public PendingAfflictionElement? FindAfflictionElement(MedicalClinic.NetAffliction target) => Afflictions.FirstOrNull(element => element.Target.Identifier.Equals(target.Identifier, StringComparison.OrdinalIgnoreCase));
public PendingAfflictionElement? FindAfflictionElement(MedicalClinic.NetAffliction target) => Afflictions.FirstOrNull(element => element.Target.Identifier == target.Identifier);
}
// Represents an affliction on the left side crew entry
@@ -268,12 +268,12 @@ namespace Barotrauma
}
int totalCost = medicalClinic.GetTotalCost();
healList.PriceBlock.Text = UpgradeStore.FormatCurrency(totalCost);
healList.PriceBlock.TextColor = GUI.Style.Red;
healList.PriceBlock.Text = TextManager.FormatCurrency(totalCost);
healList.PriceBlock.TextColor = GUIStyle.Red;
healList.HealButton.Enabled = false;
if (medicalClinic.GetMoney() > totalCost)
if (medicalClinic.GetWallet().CanAfford(totalCost))
{
healList.PriceBlock.TextColor = GUI.Style.TextColor;
healList.PriceBlock.TextColor = GUIStyle.TextColorNormal;
if (medicalClinic.PendingHeals.Any())
{
healList.HealButton.Enabled = true;
@@ -288,7 +288,7 @@ namespace Barotrauma
{
if (element.FindAfflictionElement(affliction) is { } existingAffliction)
{
existingAffliction.Price.Text = UpgradeStore.FormatCurrency(affliction.Strength);
existingAffliction.Price.Text = TextManager.FormatCurrency(affliction.Strength);
continue;
}
@@ -443,7 +443,7 @@ namespace Barotrauma
GUILayoutGroup clinicLabelLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), clinicContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
GUIImage clinicIcon = new GUIImage(new RectTransform(Vector2.One, clinicLabelLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "CrewManagementHeaderIcon", scaleToFit: true);
GUITextBlock clinicLabel = new GUITextBlock(new RectTransform(Vector2.One, clinicLabelLayout.RectTransform), TextManager.Get("medicalclinic.medicalclinic"), font: GUI.LargeFont);
GUITextBlock clinicLabel = new GUITextBlock(new RectTransform(Vector2.One, clinicLabelLayout.RectTransform), TextManager.Get("medicalclinic.medicalclinic"), font: GUIStyle.LargeFont);
GUIFrame clinicBackground = new GUIFrame(new RectTransform(Vector2.One, clinicContent.RectTransform));
@@ -459,15 +459,15 @@ namespace Barotrauma
};
GUILayoutGroup balanceLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), crewContent.RectTransform));
GUITextBlock balanceLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), TextManager.Get("campaignstore.balance"), textAlignment: Alignment.BottomRight, font: GUI.Font)
GUITextBlock balanceLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), TextManager.Get("campaignstore.balance"), textAlignment: Alignment.BottomRight, font: GUIStyle.Font)
{
AutoScaleVertical = true,
ForceUpperCase = true
ForceUpperCase = ForceUpperCase.Yes
};
GUITextBlock moneyLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), string.Empty, textAlignment: Alignment.TopRight, font: GUI.Style.SubHeadingFont)
GUITextBlock moneyLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), string.Empty, textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont)
{
TextGetter = () => UpgradeStore.FormatCurrency(medicalClinic.GetMoney()),
TextGetter = () => TextManager.FormatCurrency(medicalClinic.GetWallet().Balance),
AutoScaleVertical = true,
TextScale = 1.1f
};
@@ -519,18 +519,18 @@ namespace Barotrauma
GUILayoutGroup healthLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.1f, 1f), crewLayout.RectTransform), isHorizontal: true, Anchor.Center);
new GUITextBlock(new RectTransform(Vector2.One, healthLayout.RectTransform), string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont)
new GUITextBlock(new RectTransform(Vector2.One, healthLayout.RectTransform), string.Empty, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont)
{
TextGetter = () => $"{(int)(info.Character?.HealthPercentage ?? 100f)}%",
TextColor = GUI.Style.Green
TextGetter = () => TextManager.GetWithVariable("percentageformat", "[value]", $"{(int)(info.Character?.HealthPercentage ?? 100f)}"),
TextColor = GUIStyle.Green
};
GUITextBlock overflowIndicator =
new GUITextBlock(new RectTransform(new Vector2(0.25f, 1f), afflictionList.Content.RectTransform, scaleBasis: ScaleBasis.BothHeight), text: "+", textAlignment: Alignment.Center, font: GUI.LargeFont)
new GUITextBlock(new RectTransform(new Vector2(0.25f, 1f), afflictionList.Content.RectTransform, scaleBasis: ScaleBasis.BothHeight), text: "+", textAlignment: Alignment.Center, font: GUIStyle.LargeFont)
{
Visible = false,
CanBeFocused = false,
TextColor = GUI.Style.Red
TextColor = GUIStyle.Red
};
MedicalClinic.NetCrewMember member = new MedicalClinic.NetCrewMember { CharacterInfo = info, Afflictions = Array.Empty<MedicalClinic.NetAffliction>() };
@@ -552,13 +552,13 @@ namespace Barotrauma
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1f, 0.05f), pendingHealContainer.RectTransform), TextManager.Get("medicalclinic.pendingheals"), font: GUI.SubHeadingFont);
new GUITextBlock(new RectTransform(new Vector2(1f, 0.05f), pendingHealContainer.RectTransform), TextManager.Get("medicalclinic.pendingheals"), font: GUIStyle.SubHeadingFont);
GUIFrame healListContainer = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), pendingHealContainer.RectTransform), style: null);
GUITextBlock? errorBlock = null;
if (!GameMain.IsSingleplayer)
{
errorBlock = new GUITextBlock(new RectTransform(Vector2.One, healListContainer.RectTransform), text: TextManager.Get("pleasewaitupnp"), font: GUI.LargeFont, textAlignment: Alignment.Center);
errorBlock = new GUITextBlock(new RectTransform(Vector2.One, healListContainer.RectTransform), text: TextManager.Get("pleasewaitupnp"), font: GUIStyle.LargeFont, textAlignment: Alignment.Center);
}
GUIListBox healList = new GUIListBox(new RectTransform(Vector2.One, healListContainer.RectTransform))
@@ -571,13 +571,13 @@ namespace Barotrauma
GUILayoutGroup priceLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), footerLayout.RectTransform), isHorizontal: true);
GUITextBlock priceLabelBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), priceLayout.RectTransform), TextManager.Get("campaignstore.total"));
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), priceLayout.RectTransform), UpgradeStore.FormatCurrency(medicalClinic.GetTotalCost()), font: GUI.SubHeadingFont,
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), priceLayout.RectTransform), TextManager.FormatCurrency(medicalClinic.GetTotalCost()), font: GUIStyle.SubHeadingFont,
textAlignment: Alignment.Right);
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), footerLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight);
GUIButton healButton = new GUIButton(new RectTransform(new Vector2(0.33f, 1f), buttonLayout.RectTransform), TextManager.Get("medicalclinic.heal"))
{
Enabled = medicalClinic.PendingHeals.Any() && medicalClinic.GetTotalCost() < medicalClinic.GetMoney(),
Enabled = medicalClinic.PendingHeals.Any() && medicalClinic.GetWallet().CanAfford(medicalClinic.GetTotalCost()),
OnClicked = (button, _) =>
{
button.Enabled = false;
@@ -679,12 +679,12 @@ namespace Barotrauma
GUILayoutGroup textLayout = new GUILayoutGroup(new RectTransform(Vector2.One, parentLayout.RectTransform), isHorizontal: true);
string name = prefab.Name;
LocalizedString name = prefab.Name;
GUIFrame textContainer = new GUIFrame(new RectTransform(new Vector2(0.6f, 1f), textLayout.RectTransform), style: null);
GUITextBlock afflictionName = new GUITextBlock(new RectTransform(Vector2.One, textContainer.RectTransform), name, font: GUI.SubHeadingFont);
GUITextBlock afflictionName = new GUITextBlock(new RectTransform(Vector2.One, textContainer.RectTransform), name, font: GUIStyle.SubHeadingFont);
GUITextBlock healCost = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), textLayout.RectTransform), UpgradeStore.FormatCurrency(affliction.Price), textAlignment: Alignment.Center, font: GUI.LargeFont)
GUITextBlock healCost = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), textLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), textAlignment: Alignment.Center, font: GUIStyle.LargeFont)
{
Padding = Vector4.Zero
};
@@ -702,7 +702,7 @@ namespace Barotrauma
}
};
EnsureTextDoesntOverflow(name, afflictionName, textContainer.Rect, ImmutableArray.Create(textLayout, parentLayout));
EnsureTextDoesntOverflow(name.Value, afflictionName, textContainer.Rect, ImmutableArray.Create(textLayout, parentLayout));
healElement.Afflictions.Add(new PendingAfflictionElement(affliction, backgroundFrame, healCost));
@@ -720,8 +720,8 @@ namespace Barotrauma
GUILayoutGroup textGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.8f), parent.RectTransform));
string? characterName = info.Name,
jobName = null;
string? characterName = info.Name;
LocalizedString? jobName = null;
GUITextBlock? nameBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), textGroup.RectTransform), characterName),
jobBlock = null;
@@ -741,7 +741,7 @@ namespace Barotrauma
if (jobBlock is null) { return; }
EnsureTextDoesntOverflow(jobName, jobBlock, parent.Rect, layoutGroups);
EnsureTextDoesntOverflow(jobName?.Value, jobBlock, parent.Rect, layoutGroups);
}
}
@@ -766,14 +766,14 @@ namespace Barotrauma
mainFrame.RectTransform.ScreenSpaceOffset = new Point((int)location.X, GameMain.GraphicsHeight - mainFrame.Rect.Height);
}
GUITextBlock feedbackBlock = new GUITextBlock(new RectTransform(Vector2.One, mainFrame.RectTransform), TextManager.Get("pleasewaitupnp"), textAlignment: Alignment.Center, font: GUI.LargeFont, wrap: true)
GUITextBlock feedbackBlock = new GUITextBlock(new RectTransform(Vector2.One, mainFrame.RectTransform), TextManager.Get("pleasewaitupnp"), textAlignment: Alignment.Center, font: GUIStyle.LargeFont, wrap: true)
{
Visible = true
};
GUIButton treatAllButton = new GUIButton(new RectTransform(new Vector2(1f, 0.2f), mainLayout.RectTransform), TextManager.Get("medicalclinic.treatall"))
{
Font = GUI.SubHeadingFont,
Font = GUIStyle.SubHeadingFont,
Visible = false
};
@@ -793,7 +793,7 @@ namespace Barotrauma
if (request.Result != MedicalClinic.RequestResult.Success)
{
feedbackBlock.Text = GetErrorText(request.Result);
feedbackBlock.TextColor = GUI.Style.Red;
feedbackBlock.TextColor = GUIStyle.Red;
return;
}
@@ -844,11 +844,11 @@ namespace Barotrauma
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: GUI.SubHeadingFont);
GUITextBlock prefabBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), topTextLayout.RectTransform), prefab.Name, font: GUIStyle.SubHeadingFont);
Color textColor = Color.Lerp(GUI.Style.Orange, GUI.Style.Red, (int)affliction.AfflictionSeverity / 2f);
Color textColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red, (int)affliction.AfflictionSeverity / 2f);
string vitalityText = TextManager.GetWithVariable("medicalclinic.vitalitydifference", "[amount]", (-affliction.Strength).ToString());
LocalizedString vitalityText = TextManager.GetWithVariable("medicalclinic.vitalitydifference", "[amount]", (-affliction.Strength).ToString());
GUITextBlock vitalityBlock = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1f), topTextLayout.RectTransform), vitalityText, textAlignment: Alignment.Center)
{
TextColor = textColor,
@@ -857,8 +857,8 @@ namespace Barotrauma
AutoScaleHorizontal = true
};
string severityText = TextManager.Get($"AfflictionStrength{affliction.AfflictionSeverity}");
GUITextBlock severityBlock = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1f), topTextLayout.RectTransform), severityText, textAlignment: Alignment.Center, font: GUI.SubHeadingFont)
LocalizedString severityText = TextManager.Get($"AfflictionStrength{affliction.AfflictionSeverity}");
GUITextBlock severityBlock = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1f), topTextLayout.RectTransform), severityText, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont)
{
TextColor = textColor,
DisabledTextColor = textColor * 0.5f,
@@ -866,17 +866,17 @@ namespace Barotrauma
AutoScaleHorizontal = true
};
EnsureTextDoesntOverflow(prefab.Name, prefabBlock, prefabBlock.Rect, ImmutableArray.Create(mainLayout, topLayout, topTextLayout));
EnsureTextDoesntOverflow(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 bottomTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1f), bottomLayout.RectTransform));
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), bottomTextLayout.RectTransform), ToolBox.LimitString(prefab.Description, GUI.IntScale(64)), wrap: true)
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), bottomTextLayout.RectTransform), ToolBox.LimitString(prefab.Description, GUIStyle.Font, GUI.IntScale(64)), wrap: true)
{
ToolTip = prefab.Description
};
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), bottomTextLayout.RectTransform), UpgradeStore.FormatCurrency(affliction.Price), font: GUI.LargeFont);
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), bottomTextLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), font: GUIStyle.LargeFont);
GUIButton buyButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.75f), bottomLayout.RectTransform), style: "CrewManagementAddButton");
@@ -931,7 +931,7 @@ namespace Barotrauma
});
}
private static void EnsureTextDoesntOverflow(string? text, GUITextBlock textBlock, Rectangle bounds, ImmutableArray<GUILayoutGroup>? layoutGroups = null)
public static void EnsureTextDoesntOverflow(string? text, GUITextBlock textBlock, Rectangle bounds, ImmutableArray<GUILayoutGroup>? layoutGroups = null)
{
if (string.IsNullOrWhiteSpace(text)) { return; }
@@ -968,7 +968,7 @@ namespace Barotrauma
if (GameMain.IsSingleplayer || !(pendingHealList is { ErrorBlock: { } errorBlock, HealList: { } healList })) { return; }
errorBlock.Visible = true;
errorBlock.TextColor = GUI.Style.TextColor;
errorBlock.TextColor = GUIStyle.TextColorNormal;
errorBlock.Text = TextManager.Get("pleasewaitupnp");
healList.Visible = false;
@@ -983,7 +983,7 @@ namespace Barotrauma
if (request.Result != MedicalClinic.RequestResult.Success)
{
errorBlock.Text = GetErrorText(request.Result);
errorBlock.TextColor = GUI.Style.Red;
errorBlock.TextColor = GUIStyle.Red;
return;
}
@@ -1011,7 +1011,7 @@ namespace Barotrauma
selectedCrewAfflictionList = null;
}
private static string GetErrorText(MedicalClinic.RequestResult result)
private static LocalizedString GetErrorText(MedicalClinic.RequestResult result)
{
return result switch
{

File diff suppressed because it is too large Load Diff

View File

@@ -31,19 +31,12 @@ namespace Barotrauma
private readonly List<SubmarineInfo> subsToShow;
private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
private SubmarineInfo selectedSubmarine = null;
private string purchaseAndSwitchText, purchaseOnlyText, deliveryText, currentSubText, deliveryFeeText, priceText, switchText, missingPreviewText, currencyShorthandText, currencyLongText;
private LocalizedString purchaseAndSwitchText, purchaseOnlyText, deliveryText, currentSubText, deliveryFeeText, priceText, switchText, missingPreviewText, currencyName;
private readonly RectTransform parent;
private readonly Action closeAction;
private Sprite pageIndicator;
public static readonly string[] DeliveryTextVariables = new string[] { "[submarinename1]", "[location1]", "[location2]", "[submarinename2]", "[amount]", "[currencyname]" };
public static readonly string[] SwitchTextVariables = new string[] { "[submarinename1]", "[submarinename2]" };
public static readonly string[] PurchaseAndSwitchTextVariables = new string[] { "[submarinename1]", "[amount]", "[currencyname]", "[submarinename2]" };
public static readonly string[] PurchaseTextVariables = new string[] { "[submarinename]", "[amount]", "[currencyname]" };
private static readonly string[] notEnoughCreditsDeliveryTextVariables = new string[] { "[currencyname]", "[submarinename]", "[location1]", "[location2]" };
private static readonly string[] notEnoughCreditsPurchaseTextVariables = new string[] { "[currencyname]", "[submarinename]" };
private readonly string[] messageBoxOptions;
private readonly LocalizedString[] messageBoxOptions;
public const int DeliveryFeePerDistanceTravelled = 1000;
public static bool ContentRefreshRequired = false;
@@ -77,11 +70,11 @@ namespace Barotrauma
if (GameMain.Client == null)
{
messageBoxOptions = new string[2] { TextManager.Get("Yes"), TextManager.Get("Cancel") };
messageBoxOptions = new LocalizedString[2] { TextManager.Get("Yes"), TextManager.Get("Cancel") };
}
else
{
messageBoxOptions = new string[2] { TextManager.Get("Yes") + " " + TextManager.Get("initiatevoting"), TextManager.Get("Cancel") };
messageBoxOptions = new LocalizedString[2] { TextManager.Get("Yes") + " " + TextManager.Get("initiatevoting"), TextManager.Get("Cancel") };
}
if (Submarine.MainSub?.Info == null) { return; }
@@ -91,23 +84,19 @@ namespace Barotrauma
private void Initialize()
{
initialized = true;
currentSubText = TextManager.Get("currentsub");
deliveryFeeText = TextManager.Get("deliveryfee");
deliveryText = TextManager.Get("requestdeliverybutton");
switchText = TextManager.Get("switchtosubmarinebutton");
purchaseAndSwitchText = TextManager.Get("purchaseandswitch");
purchaseOnlyText = TextManager.Get("purchase");
priceText = TextManager.Get("price");
if (transferService)
{
deliveryFee = CalculateDeliveryFee();
currentSubText = TextManager.Get("currentsub");
deliveryFeeText = TextManager.Get("deliveryfee");
deliveryText = TextManager.Get("requestdeliverybutton");
switchText = TextManager.Get("switchtosubmarinebutton");
}
else
{
purchaseAndSwitchText = TextManager.Get("purchaseandswitch");
purchaseOnlyText = TextManager.Get("purchase");
priceText = TextManager.Get("price");
}
currencyShorthandText = TextManager.Get("currencyformat");
currencyLongText = TextManager.Get("credit").ToLower();
currencyName = TextManager.Get("credit").Value.ToLowerInvariant();
UpdateSubmarines();
missingPreviewText = TextManager.Get("SubPreviewImageNotFound");
@@ -135,9 +124,9 @@ namespace Barotrauma
};
content = new GUILayoutGroup(new RectTransform(new Point(background.Rect.Width - HUDLayoutSettings.Padding * 4, background.Rect.Height - HUDLayoutSettings.Padding * 4), background.RectTransform, Anchor.Center)) { AbsoluteSpacing = (int)(HUDLayoutSettings.Padding * 1.5f) };
GUITextBlock header = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), content.RectTransform), transferService ? TextManager.Get("switchsubmarineheader") : TextManager.GetWithVariable("outpostshipyard", "[location]", GameMain.GameSession.Map.CurrentLocation.Name), font: GUI.LargeFont);
GUITextBlock header = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), content.RectTransform), transferService ? TextManager.Get("switchsubmarineheader") : TextManager.GetWithVariable("outpostshipyard", "[location]", GameMain.GameSession.Map.CurrentLocation.Name), font: GUIStyle.LargeFont);
header.CalculateHeightFromText(0, true);
GUITextBlock credits = new GUITextBlock(new RectTransform(Vector2.One, header.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight)
GUITextBlock credits = new GUITextBlock(new RectTransform(Vector2.One, header.RectTransform), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight)
{
TextGetter = CampaignUI.GetMoney
};
@@ -159,7 +148,7 @@ namespace Barotrauma
specsFrame = new GUIListBox(new RectTransform(new Vector2(0.39f, 1f), infoFrame.RectTransform), style: null) { Spacing = GUI.IntScale(5), Padding = new Vector4(HUDLayoutSettings.Padding / 2f, HUDLayoutSettings.Padding, 0, 0) };
new GUIFrame(new RectTransform(new Vector2(0.02f, 0.8f), infoFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.1f) }, style: "VerticalLine");
GUIListBox descriptionFrame = new GUIListBox(new RectTransform(new Vector2(0.59f, 1f), infoFrame.RectTransform), style: null) { Padding = new Vector4(HUDLayoutSettings.Padding / 2f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding / 2f) };
descriptionTextBlock = new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionFrame.Content.RectTransform), string.Empty, font: GUI.Font, wrap: true) { CanBeFocused = false };
descriptionTextBlock = new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionFrame.Content.RectTransform), string.Empty, font: GUIStyle.Font, wrap: true) { CanBeFocused = false };
GUILayoutGroup buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), content.RectTransform), childAnchor: Anchor.CenterRight) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
@@ -180,7 +169,7 @@ namespace Barotrauma
SetConfirmButtonState(false);
pageIndicatorHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.5f), submarineControlsGroup.RectTransform), style: null);
pageIndicator = GUI.Style.GetComponentStyle("GUIPageIndicator").GetDefaultSprite();
pageIndicator = GUIStyle.GetComponentStyle("GUIPageIndicator").GetDefaultSprite();
UpdatePaging();
for (int i = 0; i < submarineDisplays.Length; i++)
@@ -191,9 +180,9 @@ namespace Barotrauma
};
submarineDisplayElement.submarineImage = new GUIImage(new RectTransform(new Vector2(0.8f, 1f), submarineDisplayElement.background.RectTransform, Anchor.Center), null, true);
submarineDisplayElement.middleTextBlock = new GUITextBlock(new RectTransform(new Vector2(0.8f, 1f), submarineDisplayElement.background.RectTransform, Anchor.Center), string.Empty, textAlignment: Alignment.Center);
submarineDisplayElement.submarineName = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
submarineDisplayElement.submarineClass = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding + (int)GUI.Font.MeasureString(submarineDisplayElement.submarineName.Text).Y) }, string.Empty, textAlignment: Alignment.Center);
submarineDisplayElement.submarineFee = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
submarineDisplayElement.submarineName = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont);
submarineDisplayElement.submarineClass = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding + (int)GUIStyle.Font.MeasureString(submarineDisplayElement.submarineName.Text).Y) }, string.Empty, textAlignment: Alignment.Center);
submarineDisplayElement.submarineFee = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont);
submarineDisplayElement.selectSubmarineButton = new GUIButton(new RectTransform(Vector2.One, submarineDisplayElement.background.RectTransform), style: null);
submarineDisplayElement.previewButton = new GUIButton(new RectTransform(Vector2.One * 0.12f, submarineDisplayElement.background.RectTransform, anchor: Anchor.BottomRight, pivot: Pivot.BottomRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point((int)(0.03f * background.Rect.Height)) }, style: "ExpandButton")
{
@@ -342,7 +331,7 @@ namespace Barotrauma
if (!GameMain.GameSession.IsSubmarineOwned(subToDisplay))
{
string amountString = currencyShorthandText.Replace("[credits]", subToDisplay.Price.ToString());
LocalizedString amountString = TextManager.FormatCurrency(subToDisplay.Price);
submarineDisplays[i].submarineFee.Text = priceText.Replace("[amount]", amountString).Replace("[currencyname]", string.Empty).TrimEnd();
}
else
@@ -351,7 +340,7 @@ namespace Barotrauma
{
if (deliveryFee > 0)
{
string amountString = currencyShorthandText.Replace("[credits]", deliveryFee.ToString());
LocalizedString amountString = TextManager.FormatCurrency(deliveryFee);
submarineDisplays[i].submarineFee.Text = deliveryFeeText.Replace("[amount]", amountString).Replace("[currencyname]", string.Empty).TrimEnd();
}
else
@@ -535,7 +524,7 @@ namespace Barotrauma
listBackground.Sprite = previewImage;
listBackground.SetCrop(true);
ScalableFont font = GUI.Font;
GUIFont font = GUIStyle.Font;
info.CreateSpecsWindow(specsFrame, font);
descriptionTextBlock.Text = info.Description;
descriptionTextBlock.CalculateHeightFromText();
@@ -588,10 +577,13 @@ namespace Barotrauma
private void ShowTransferPrompt()
{
if (GameMain.GameSession.Campaign.Money < deliveryFee && deliveryFee > 0)
if (!GameMain.GameSession.Campaign.Wallet.CanAfford(deliveryFee) && deliveryFee > 0)
{
new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("notenoughmoneyfordeliverytext", notEnoughCreditsDeliveryTextVariables,
new string[] { currencyLongText, selectedSubmarine.DisplayName, deliveryLocationName, GameMain.GameSession.Map.CurrentLocation.Name }));
new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("notenoughmoneyfordeliverytext",
("[currencyname]", currencyName),
("[submarinename]", selectedSubmarine.DisplayName),
("[location1]", deliveryLocationName),
("[location2]", GameMain.GameSession.Map.CurrentLocation.Name)));
return;
}
@@ -599,13 +591,19 @@ namespace Barotrauma
if (deliveryFee > 0)
{
msgBox = new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("deliveryrequesttext", DeliveryTextVariables,
new string[6] { selectedSubmarine.DisplayName, deliveryLocationName, GameMain.GameSession.Map.CurrentLocation.Name, CurrentOrPendingSubmarine().DisplayName, deliveryFee.ToString(), currencyLongText }), messageBoxOptions);
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);
}
else
{
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), TextManager.GetWithVariables("switchsubmarinetext", SwitchTextVariables,
new string[2] { CurrentOrPendingSubmarine().DisplayName, selectedSubmarine.DisplayName }), messageBoxOptions);
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), TextManager.GetWithVariables("switchsubmarinetext",
("[submarinename1]", CurrentOrPendingSubmarine().DisplayName),
("[submarinename2]", selectedSubmarine.DisplayName)), messageBoxOptions);
}
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
@@ -627,10 +625,11 @@ namespace Barotrauma
private void ShowBuyPrompt(bool purchaseOnly)
{
if (GameMain.GameSession.Campaign.Money < selectedSubmarine.Price)
if (!GameMain.GameSession.Campaign.Wallet.CanAfford(selectedSubmarine.Price))
{
new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext", notEnoughCreditsPurchaseTextVariables,
new string[2] { currencyLongText, selectedSubmarine.DisplayName }));
new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext",
("[currencyname]", currencyName),
("[submarinename]", selectedSubmarine.DisplayName)));
return;
}
@@ -638,8 +637,11 @@ namespace Barotrauma
if (!purchaseOnly)
{
msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), TextManager.GetWithVariables("purchaseandswitchsubmarinetext", PurchaseAndSwitchTextVariables,
new string[4] { selectedSubmarine.DisplayName, selectedSubmarine.Price.ToString(), currencyLongText, CurrentOrPendingSubmarine().DisplayName }), messageBoxOptions);
msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), TextManager.GetWithVariables("purchaseandswitchsubmarinetext",
("[submarinename1]", selectedSubmarine.DisplayName),
("[amount]", selectedSubmarine.Price.ToString()),
("[currencyname]", currencyName),
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName)), messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
@@ -658,8 +660,10 @@ namespace Barotrauma
}
else
{
msgBox = new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("purchasesubmarinetext", PurchaseTextVariables,
new string[3] { selectedSubmarine.DisplayName, selectedSubmarine.Price.ToString(), currencyLongText }), messageBoxOptions);
msgBox = new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("purchasesubmarinetext",
("[submarinename]", selectedSubmarine.DisplayName),
("[amount]", selectedSubmarine.Price.ToString()),
("[currencyname]", currencyName)), messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{

File diff suppressed because it is too large Load Diff

View File

@@ -19,11 +19,7 @@ namespace Barotrauma
private set;
}
public bool Slice
{
get;
set;
}
public bool Slice => Slices != null;
public Rectangle[] Slices
{
@@ -54,7 +50,7 @@ namespace Barotrauma
public TransitionMode TransitionMode { get; private set; }
public UISprite(XElement element)
public UISprite(ContentXElement element)
{
Sprite = new Sprite(element);
MaintainAspectRatio = element.GetAttributeBool("maintainaspectratio", false);
@@ -69,6 +65,7 @@ namespace Barotrauma
}
Vector4 sliceVec = element.GetAttributeVector4("slice", Vector4.Zero);
Slices = null;
if (sliceVec != Vector4.Zero)
{
minBorderScale = element.GetAttributeFloat("minborderscale", 0.1f);
@@ -76,7 +73,6 @@ namespace Barotrauma
Rectangle slice = new Rectangle((int)sliceVec.X, (int)sliceVec.Y, (int)(sliceVec.Z - sliceVec.X), (int)(sliceVec.W - sliceVec.Y));
Slice = true;
Slices = new Rectangle[9];
//top-left

View File

@@ -41,7 +41,7 @@ namespace Barotrauma
private readonly CampaignUI campaignUI;
private CampaignMode? Campaign => campaignUI.Campaign;
private int AvailableMoney => Campaign?.Money ?? 0;
private Wallet PlayerWallet => Campaign?.Wallet ?? Wallet.Invalid;
private UpgradeTab selectedUpgradeTab = UpgradeTab.Upgrade;
private GUIMessageBox? currectConfirmation;
@@ -61,7 +61,7 @@ namespace Barotrauma
private Vector2[][] subHullVertices = new Vector2[0][];
private List<Structure> submarineWalls = new List<Structure>();
public MapEntity? HoveredItem;
public MapEntity? HoveredEntity;
private bool highlightWalls;
private UpgradeCategory? currentUpgradeCategory;
@@ -73,6 +73,8 @@ namespace Barotrauma
private Point screenResolution;
private bool needsRefresh = true;
/// <summary>
/// While set to true any call to <see cref="RefreshUpgradeList"/> will cause the buy button to be disabled and to not update the prices.
/// This is to prevent us from buying another upgrade before the server has given us the new prices and causing potential syncing issues.
@@ -102,12 +104,18 @@ namespace Barotrauma
CreateUI(upgradeFrame);
if (Campaign == null) { return; }
Campaign.UpgradeManager.OnUpgradesChanged += RefreshAll;
Campaign.CargoManager.OnPurchasedItemsChanged += RefreshAll;
Campaign.CargoManager.OnSoldItemsChanged += RefreshAll;
Campaign.UpgradeManager.OnUpgradesChanged += RequestRefresh;
Campaign.CargoManager.OnPurchasedItemsChanged += RequestRefresh;
Campaign.CargoManager.OnSoldItemsChanged += RequestRefresh;
Campaign.OnMoneyChanged.RegisterOverwriteExisting(nameof(UpgradeStore).ToIdentifier(), e => { RequestRefresh(); } );
}
public void RefreshAll()
public void RequestRefresh()
{
needsRefresh = true;
}
private void RefreshAll()
{
switch (selectedUpgradeTab)
{
@@ -130,6 +138,7 @@ namespace Barotrauma
}
break;
}
needsRefresh = false;
}
private void RefreshUpgradeList()
@@ -184,7 +193,7 @@ namespace Barotrauma
}
// reset the order first
foreach (UpgradeCategory category in UpgradeCategory.Categories)
foreach (UpgradeCategory category in UpgradeCategory.Categories.OrderBy(c => c.Name))
{
GUIComponent component = categoryList.Content.FindChild(c => c.UserData is CategoryData categoryData && categoryData.Category == category);
component?.SetAsLastChild();
@@ -245,7 +254,7 @@ namespace Barotrauma
* |----------------------------|
*/
GUILayoutGroup tooltipLayout = new GUILayoutGroup(rectT(0.95f,0.95f, ItemInfoFrame, Anchor.Center)) { Stretch = true };
new GUITextBlock(rectT(1, 0, tooltipLayout), string.Empty, font: GUI.SubHeadingFont) { UserData = "itemname" };
new GUITextBlock(rectT(1, 0, tooltipLayout), string.Empty, font: GUIStyle.SubHeadingFont) { UserData = "itemname" };
new GUITextBlock(rectT(1, 0, tooltipLayout), TextManager.Get("UpgradeUITooltip.UpgradeListHeader"));
new GUIListBox(rectT(1, 0.5f, tooltipLayout), style: null) { ScrollBarVisible = false, AutoHideScrollBar = false, SmoothScroll = true, UserData = "upgradelist"};
new GUITextBlock(rectT(1, 0, tooltipLayout), string.Empty) { UserData = "moreindicator" };
@@ -268,7 +277,7 @@ namespace Barotrauma
GUILayoutGroup leftLayout = new GUILayoutGroup(rectT(0.5f, 1, topHeaderLayout)) { RelativeSpacing = 0.05f };
GUILayoutGroup locationLayout = new GUILayoutGroup(rectT(1, 0.5f, leftLayout), isHorizontal: true);
GUIImage submarineIcon = new GUIImage(rectT(new Point(locationLayout.Rect.Height, locationLayout.Rect.Height), locationLayout), style: "SubmarineIcon", scaleToFit: true);
new GUITextBlock(rectT(1.0f - submarineIcon.RectTransform.RelativeSize.X, 1, locationLayout), TextManager.Get("UpgradeUI.Title"), font: GUI.LargeFont);
new GUITextBlock(rectT(1.0f - submarineIcon.RectTransform.RelativeSize.X, 1, locationLayout), TextManager.Get("UpgradeUI.Title"), font: GUIStyle.LargeFont);
categoryButtonLayout = new GUILayoutGroup(rectT(0.4f, 0.3f, leftLayout), isHorizontal: true) { Stretch = true };
GUIButton upgradeButton = new GUIButton(rectT(1, 1f, categoryButtonLayout), TextManager.Get("UICategory.Upgrades"), style: "GUITabButton") { UserData = UpgradeTab.Upgrade, Selected = selectedUpgradeTab == UpgradeTab.Upgrade };
GUIButton repairButton = new GUIButton(rectT(1, 1f, categoryButtonLayout), TextManager.Get("UICategory.Maintenance"), style: "GUITabButton") { UserData = UpgradeTab.Repairs, Selected = selectedUpgradeTab == UpgradeTab.Repairs };
@@ -285,9 +294,9 @@ namespace Barotrauma
*/
GUILayoutGroup rightLayout = new GUILayoutGroup(rectT(0.5f, 1, topHeaderLayout), childAnchor: Anchor.TopRight);
GUILayoutGroup priceLayout = new GUILayoutGroup(rectT(1, 0.8f, rightLayout), childAnchor: Anchor.Center) { RelativeSpacing = 0.08f };
new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.Get("CampaignStore.Balance"), font: GUI.SubHeadingFont, textAlignment: Alignment.Right);
new GUITextBlock(rectT(1f, 0f, priceLayout), FormatCurrency(AvailableMoney, format: true), font: GUI.SubHeadingFont, textAlignment: Alignment.Right) { TextGetter = () => FormatCurrency(AvailableMoney, format: true) };
new GUIFrame(rectT(0.5f, 0.1f, rightLayout, Anchor.BottomRight), style: "HorizontalLine") { IgnoreLayoutGroups = true };
new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.Get("CampaignStore.Balance"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right);
new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.FormatCurrency(PlayerWallet.Balance), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right) { TextGetter = () => TextManager.FormatCurrency(PlayerWallet.Balance) };
new GUIFrame(rectT(0.5f, 0.1f, rightLayout, Anchor.BottomRight), style: "HorizontalLine") { IgnoreLayoutGroups = true };
repairButton.OnClicked = upgradeButton.OnClicked = (button, o) =>
{
@@ -343,15 +352,15 @@ namespace Barotrauma
private void DrawItemSwapPreview(SpriteBatch spriteBatch, GUICustomComponent component)
{
var selectedItem = customizeTabOpen ?
activeItemSwapSlideDown?.UserData as Item ?? HoveredItem as Item :
HoveredItem as Item;
activeItemSwapSlideDown?.UserData as Item ?? HoveredEntity as Item :
HoveredEntity as Item;
if (selectedItem?.Prefab.SwappableItem == null) { return; }
Sprite schematicsSprite = selectedItem.Prefab.SwappableItem.SchematicSprite;
if (schematicsSprite == null) { return; }
float schematicsScale = Math.Min(component.Rect.Width / 2 / schematicsSprite.size.X, component.Rect.Height / schematicsSprite.size.Y);
Vector2 center = new Vector2(component.Rect.Center.X, component.Rect.Center.Y);
schematicsSprite.Draw(spriteBatch, new Vector2(component.Rect.X, center.Y), GUI.Style.Green, new Vector2(0, schematicsSprite.size.Y / 2),
schematicsSprite.Draw(spriteBatch, new Vector2(component.Rect.X, center.Y), GUIStyle.Green, new Vector2(0, schematicsSprite.size.Y / 2),
scale: schematicsScale);
var swappableItemList = selectedUpgradeCategoryLayout?.FindChild("prefablist", true) as GUIListBox;
@@ -359,10 +368,10 @@ namespace Barotrauma
ItemPrefab swapTo = highlightedElement?.UserData as ItemPrefab ?? selectedItem.PendingItemSwap;
if (swapTo?.SwappableItem == null) { return; }
Sprite? schematicsSprite2 = swapTo.SwappableItem?.SchematicSprite;
schematicsSprite2?.Draw(spriteBatch, new Vector2(component.Rect.Right, center.Y), GUI.Style.Orange, new Vector2(schematicsSprite2.size.X, schematicsSprite2.size.Y / 2),
schematicsSprite2?.Draw(spriteBatch, new Vector2(component.Rect.Right, center.Y), GUIStyle.Orange, new Vector2(schematicsSprite2.size.X, schematicsSprite2.size.Y / 2),
scale: Math.Min(component.Rect.Width / 2 / schematicsSprite2.size.X, component.Rect.Height / schematicsSprite2.size.Y));
var arrowSprite = GUI.Style?.GetComponentStyle("GUIButtonToggleRight")?.GetDefaultSprite();
var arrowSprite = GUIStyle.GetComponentStyle("GUIButtonToggleRight")?.GetDefaultSprite();
if (arrowSprite != null)
{
arrowSprite.Draw(spriteBatch, center, scale: GUI.Scale);
@@ -426,14 +435,14 @@ namespace Barotrauma
return false;
}
if (AvailableMoney >= hullRepairCost)
if (PlayerWallet.CanAfford(hullRepairCost))
{
string body = TextManager.GetWithVariable("WallRepairs.PurchasePromptBody", "[amount]", hullRepairCost.ToString());
LocalizedString body = TextManager.GetWithVariable("WallRepairs.PurchasePromptBody", "[amount]", hullRepairCost.ToString());
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
{
if (AvailableMoney >= hullRepairCost)
if (PlayerWallet.Balance >= hullRepairCost)
{
Campaign.Money -= hullRepairCost;
PlayerWallet.TryDeduct(hullRepairCost);
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
Campaign.PurchasedHullRepairs = true;
button.Enabled = false;
@@ -461,14 +470,14 @@ namespace Barotrauma
CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallitems"), "RepairItemsButton", itemRepairCost, (button, o) =>
{
if (AvailableMoney >= itemRepairCost && !Campaign.PurchasedItemRepairs)
if (PlayerWallet.Balance >= itemRepairCost && !Campaign.PurchasedItemRepairs)
{
string body = TextManager.GetWithVariable("ItemRepairs.PurchasePromptBody", "[amount]", itemRepairCost.ToString());
LocalizedString body = TextManager.GetWithVariable("ItemRepairs.PurchasePromptBody", "[amount]", itemRepairCost.ToString());
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
{
if (AvailableMoney >= itemRepairCost && !Campaign.PurchasedItemRepairs)
if (PlayerWallet.Balance >= itemRepairCost && !Campaign.PurchasedItemRepairs)
{
Campaign.Money -= itemRepairCost;
PlayerWallet.TryDeduct(itemRepairCost);
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
Campaign.PurchasedItemRepairs = true;
button.Enabled = false;
@@ -493,7 +502,7 @@ namespace Barotrauma
{
foreach (var (item, itemFrame) in itemPreviews)
{
itemFrame.OutlineColor = itemFrame.Color = isHovered && item.GetComponent<DockingPort>() == null ? GUI.Style.Orange : previewWhite;
itemFrame.OutlineColor = itemFrame.Color = isHovered && item.GetComponent<DockingPort>() == null ? GUIStyle.Orange : previewWhite;
}
return true;
});
@@ -507,14 +516,14 @@ namespace Barotrauma
return false;
}
if (AvailableMoney >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
if (PlayerWallet.CanAfford(shuttleRetrieveCost) && !Campaign.PurchasedLostShuttles)
{
string body = TextManager.GetWithVariable("ReplaceLostShuttles.PurchasePromptBody", "[amount]", shuttleRetrieveCost.ToString());
LocalizedString body = TextManager.GetWithVariable("ReplaceLostShuttles.PurchasePromptBody", "[amount]", shuttleRetrieveCost.ToString());
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
{
if (AvailableMoney >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
if (PlayerWallet.Balance >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
{
Campaign.Money -= shuttleRetrieveCost;
PlayerWallet.TryDeduct(shuttleRetrieveCost);
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
Campaign.PurchasedLostShuttles = true;
button.Enabled = false;
@@ -540,7 +549,7 @@ namespace Barotrauma
{
if (subInfo.LeftBehindDockingPortIDs.Contains(item.ID))
{
itemFrame.OutlineColor = itemFrame.Color = subInfo.BlockedDockingPortIDs.Contains(item.ID) ? GUI.Style.Red : GUI.Style.Green;
itemFrame.OutlineColor = itemFrame.Color = subInfo.BlockedDockingPortIDs.Contains(item.ID) ? GUIStyle.Red : GUIStyle.Green;
}
else
{
@@ -551,7 +560,7 @@ namespace Barotrauma
}, disableElement: true);
}
private void CreateRepairEntry(GUIComponent parent, string title, string imageStyle, int price, GUIButton.OnClickedHandler onPressed, bool isDisabled, Func<bool, bool>? onHover = null, bool disableElement = false)
private void CreateRepairEntry(GUIComponent parent, LocalizedString title, string imageStyle, int price, GUIButton.OnClickedHandler onPressed, bool isDisabled, Func<bool, bool>? onHover = null, bool disableElement = false)
{
GUIFrame frameChild = new GUIFrame(rectT(new Point(parent.Rect.Width, (int) (96 * GUI.Scale)), parent), style: "UpgradeUIFrame");
frameChild.SelectedColor = frameChild.Color;
@@ -569,16 +578,16 @@ namespace Barotrauma
GUILayoutGroup contentLayout = new GUILayoutGroup(rectT(0.9f, 0.85f, frameChild, Anchor.Center), isHorizontal: true);
var repairIcon = new GUIFrame(rectT(new Point(contentLayout.Rect.Height, contentLayout.Rect.Height), contentLayout), style: imageStyle);
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - repairIcon.RectTransform.RelativeSize.X, 1, contentLayout)) { Stretch = true };
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUI.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
new GUITextBlock(rectT(1, 0, textLayout), FormatCurrency(price));
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
new GUITextBlock(rectT(1, 0, textLayout), TextManager.FormatCurrency(price));
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = "buybutton" };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = AvailableMoney >= price && !isDisabled, OnClicked = onPressed };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = PlayerWallet.Balance >= price && !isDisabled, OnClicked = onPressed };
contentLayout.Recalculate();
buyButtonLayout.Recalculate();
if (disableElement)
{
frameChild.Enabled = AvailableMoney >= price && !isDisabled;
frameChild.Enabled = PlayerWallet.Balance >= price && !isDisabled;
}
if (!HasPermission)
@@ -610,9 +619,9 @@ namespace Barotrauma
Dictionary<UpgradeCategory, List<UpgradePrefab>> upgrades = new Dictionary<UpgradeCategory, List<UpgradePrefab>>();
foreach (UpgradeCategory category in UpgradeCategory.Categories)
foreach (UpgradeCategory category in UpgradeCategory.Categories.OrderBy(c => c.Name))
{
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs)
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs.OrderBy(p => p.Name))
{
if (prefab.UpgradeCategories.Contains(category))
{
@@ -661,7 +670,7 @@ namespace Barotrauma
* |-----------------------------|--------------------------|
*/
GUILayoutGroup contentLayout = new GUILayoutGroup(rectT(0.9f, 0.85f, frameChild, Anchor.Center));
var itemCategoryLabel = new GUITextBlock(rectT(1, 1, contentLayout), category.Name, font: GUI.SubHeadingFont) { CanBeFocused = false };
var itemCategoryLabel = new GUITextBlock(rectT(1, 1, contentLayout), category.Name, font: GUIStyle.SubHeadingFont) { CanBeFocused = false };
GUILayoutGroup indicatorLayout = new GUILayoutGroup(rectT(0.5f, 0.25f, contentLayout, Anchor.BottomRight), isHorizontal: true, childAnchor: Anchor.TopRight) { UserData = "indicators", IgnoreLayoutGroups = true, RelativeSpacing = 0.01f };
foreach (var prefab in prefabs)
@@ -742,7 +751,7 @@ namespace Barotrauma
GUIComponent[] categoryFrames = GetFrames(category);
foreach (GUIComponent itemFrame in itemPreviews.Values)
{
itemFrame.OutlineColor = itemFrame.Color = categoryFrames.Contains(itemFrame) ? GUI.Style.Orange : previewWhite;
itemFrame.OutlineColor = itemFrame.Color = categoryFrames.Contains(itemFrame) ? GUIStyle.Orange : previewWhite;
itemFrame.Children.ForEach(c => c.Color = itemFrame.Color);
}
@@ -790,7 +799,7 @@ namespace Barotrauma
GUIComponent[] categoryFrames = GetFrames(category);
foreach (GUIComponent itemFrame in itemPreviews.Values)
{
itemFrame.OutlineColor = itemFrame.Color = categoryFrames.Contains(itemFrame) ? GUI.Style.Orange : previewWhite;
itemFrame.OutlineColor = itemFrame.Color = categoryFrames.Contains(itemFrame) ? GUIStyle.Orange : previewWhite;
itemFrame.Children.ForEach(c => c.Color = itemFrame.Color);
}
return true;
@@ -851,8 +860,8 @@ namespace Barotrauma
if (linkedItems.Min(it => it.ID) < item.ID) { return; }
var currentOrPending = item.PendingItemSwap ?? item.Prefab;
string name = currentOrPending.Name;
string nameWithQuantity = "";
LocalizedString name = currentOrPending.Name;
LocalizedString nameWithQuantity = "";
if (linkedItems.Count > 1)
{
foreach (ItemPrefab distinctItem in linkedItems.Select(it => it.Prefab).Distinct())
@@ -881,7 +890,7 @@ namespace Barotrauma
};
GUILayoutGroup buttonLayout = new GUILayoutGroup(rectT(1f, 1f, toggleButton.Frame), isHorizontal: true);
string slotText = "";
LocalizedString slotText = "";
if (linkedItems.Count > 1)
{
slotText = TextManager.GetWithVariable("weaponslot", "[number]", string.Join(", ", linkedItems.Select(it => (swappableEntities.IndexOf(it) + 1).ToString())));
@@ -891,13 +900,13 @@ namespace Barotrauma
slotText = TextManager.GetWithVariable("weaponslot", "[number]", (swappableEntities.IndexOf(item) + 1).ToString());
}
new GUITextBlock(rectT(0.3f, 1f, buttonLayout), text: slotText, font: GUI.SubHeadingFont);
new GUITextBlock(rectT(0.3f, 1f, buttonLayout), text: slotText, font: GUIStyle.SubHeadingFont);
GUILayoutGroup group = new GUILayoutGroup(rectT(0.7f, 1f, buttonLayout), isHorizontal: true) { Stretch = true };
string title = item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : nameWithQuantity;
GUITextBlock text = new GUITextBlock(rectT(0.7f, 1f, group), text: title, font: GUI.SubHeadingFont, textAlignment: Alignment.Right, parseRichText: true)
var title = item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : nameWithQuantity;
GUITextBlock text = new GUITextBlock(rectT(0.7f, 1f, group), text: RichString.Rich(title), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right)
{
TextColor = GUI.Style.Orange
TextColor = GUIStyle.Orange
};
GUIImage arrowImage = new GUIImage(rectT(0.5f, 1f, group, scaleBasis: ScaleBasis.BothHeight), style: "SlideDownArrow", scaleToFit: true);
@@ -911,7 +920,7 @@ namespace Barotrauma
List<GUIFrame> frames = new List<GUIFrame>();
if (currentOrPending != null)
{
bool canUninstall = item.PendingItemSwap != null || !string.IsNullOrEmpty(currentOrPending.SwappableItem?.ReplacementOnUninstall);
bool canUninstall = item.PendingItemSwap != null || !(currentOrPending.SwappableItem?.ReplacementOnUninstall.IsEmpty ?? true);
bool isUninstallPending = item.Prefab.SwappableItem != null && item.PendingItemSwap?.Identifier == item.Prefab.SwappableItem.ReplacementOnUninstall;
if (isUninstallPending) { canUninstall = false; }
@@ -928,9 +937,7 @@ namespace Barotrauma
{
string textTag = item.PendingItemSwap != null ? "upgrades.cancelitemswappromptbody" : "upgrades.itemuninstallpromptbody";
if (isUninstallPending) { textTag = "upgrades.cancelitemuninstallpromptbody"; }
string promptBody = TextManager.GetWithVariables(textTag,
new[] { "[itemtouninstall]" },
new[] { isUninstallPending ? item.Name : currentOrPending.Name });
LocalizedString promptBody = TextManager.GetWithVariable(textTag, "[itemtouninstall]", isUninstallPending ? item.Name : currentOrPending.Name);
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("upgrades.refundprompttitle"), promptBody, () =>
{
if (GameMain.NetworkMember != null)
@@ -965,14 +972,14 @@ namespace Barotrauma
buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton"));
if (!(frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton buyButton)) { continue; }
if (Campaign.Money >= price)
if (PlayerWallet.CanAfford(price))
{
buyButton.Enabled = true;
buyButton.OnClicked += (button, o) =>
{
string promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody",
new[] { "[itemtoinstall]", "[amount]" },
new[] { replacement.Name, (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count).ToString() });
LocalizedString promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody",
("[itemtoinstall]", replacement.Name),
("[amount]", (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count).ToString()));
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
{
if (GameMain.NetworkMember != null)
@@ -1019,7 +1026,7 @@ namespace Barotrauma
var linkedItems = Campaign.UpgradeManager.GetLinkedItemsToSwap(item);
foreach (var itemPreview in itemPreviews)
{
itemPreview.Value.OutlineColor = itemPreview.Value.Color = linkedItems.Contains(itemPreview.Key) ? GUI.Style.Orange : previewWhite;
itemPreview.Value.OutlineColor = itemPreview.Value.Color = linkedItems.Contains(itemPreview.Key) ? GUIStyle.Orange : previewWhite;
}
foreach (GUIComponent otherComponent in toggleButton.Parent.Children)
{
@@ -1041,7 +1048,7 @@ namespace Barotrauma
foreach (var itemPreview in itemPreviews)
{
if (currentStoreLayout?.SelectedData is CategoryData categoryData && !categoryData.Category.ItemTags.Any(t => itemPreview.Key.HasTag(t))) { continue; }
itemPreview.Value.OutlineColor = itemPreview.Value.Color = GUI.Style.Orange;
itemPreview.Value.OutlineColor = itemPreview.Value.Color = GUIStyle.Orange;
}
}
activeItemSwapSlideDown = toggleButton.Selected ? toggleButton : null;
@@ -1058,7 +1065,7 @@ namespace Barotrauma
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
}
public static GUIFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, string title, string body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton", UpgradePrefab upgradePrefab = null, int currentLevel = 0)
public static GUIFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, LocalizedString title, LocalizedString body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton", UpgradePrefab? upgradePrefab = null, int currentLevel = 0)
{
float progressBarHeight = 0.25f;
@@ -1080,29 +1087,29 @@ namespace Barotrauma
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 };
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), title, font: GUI.SubHeadingFont, parseRichText: true) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
var description = new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUI.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? buyButtonLayout = null;
if (addProgressBar)
{
progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = "progressbar" };
new GUIProgressBar(rectT(0.8f, 0.75f, progressLayout), 0.0f, GUI.Style.Orange);
new GUITextBlock(rectT(0.2f, 1, progressLayout), string.Empty, font: GUI.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
new GUIProgressBar(rectT(0.8f, 0.75f, progressLayout), 0.0f, GUIStyle.Orange);
new GUITextBlock(rectT(0.2f, 1, progressLayout), string.Empty, font: GUIStyle.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
}
if (addBuyButton)
{
string formattedPrice = FormatCurrency(Math.Abs(price));
var formattedPrice = TextManager.FormatCurrency(Math.Abs(price));
//negative price = refund
if (price < 0) { formattedPrice = "+" + formattedPrice; }
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
var priceText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center);
if (price < 0)
{
priceText.TextColor = GUI.Style.Green;
priceText.TextColor = GUIStyle.Green;
}
else if (price == 0)
{
@@ -1120,8 +1127,8 @@ namespace Barotrauma
// cut the description if it overflows and add a tooltip to it
for (int i = 100; i > 0 && description.Rect.Height > descriptionLayout.Rect.Height; i--)
{
string[] lines = description.WrappedText.Split('\n');
var newString = string.Join('\n', lines.Take(lines.Length - 1));
var lines = description.WrappedText.Split('\n');
var newString = string.Join('\n', lines.Take(lines.Count - 1));
if (0 >= newString.Length - 4) { break; }
description.Text = newString.Substring(0, newString.Length - 4) + "...";
@@ -1185,7 +1192,9 @@ namespace Barotrauma
buyButton.OnClicked += (button, o) =>
{
string promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody", new []{ "[upgradename]", "[amount]"}, new []{ prefab.Name, prefab.Price.GetBuyprice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation).ToString() });
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
("[upgradename]", prefab.Name),
("[amount]", prefab.Price.GetBuyprice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation).ToString()));
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
{
if (GameMain.NetworkMember != null)
@@ -1225,7 +1234,7 @@ namespace Barotrauma
itemName.Text = entity is Item ? entity.Name : TextManager.Get("upgradecategory.walls");
if (slotIndex > -1)
{
itemName.Text = TextManager.GetWithVariables("weaponslotwithname", new string[] { "[number]", "[weaponname]" }, new string[] { slotIndex.ToString(), itemName.Text });
itemName.Text = TextManager.GetWithVariables("weaponslotwithname", ("[number]", slotIndex.ToString()), ("[weaponname]", itemName.Text));
}
upgradeList.Content.ClearChildren();
for (var i = 0; i < upgrades.Count && i < maxUpgrades; i++)
@@ -1246,7 +1255,7 @@ namespace Barotrauma
{
if (textBlock.UserData is Tuple<int, UpgradePrefab> tuple && tuple.Item2 == prefab)
{
string tooltip = CreateListEntry(tuple.Item2.Name, level + tuple.Item1);
var tooltip = CreateListEntry(tuple.Item2.Name, level + tuple.Item1);
textBlock.Text = tooltip;
found = true;
break;
@@ -1275,7 +1284,7 @@ namespace Barotrauma
moreIndicator.CalculateHeightFromText();
layout.Recalculate();
static string CreateListEntry(string name, int level) => TextManager.GetWithVariables("upgradeuitooltip.upgradelistelement", new[] { "[upgradename]", "[level]" }, new[] { name, $"{level}" });
static LocalizedString CreateListEntry(LocalizedString name, int level) => TextManager.GetWithVariables("upgradeuitooltip.upgradelistelement", ("[upgradename]", name), ("[level]", $"{level}"));
}
public static IEnumerable<UpgradeCategory> GetApplicableCategories(Submarine drawnSubmarine)
@@ -1294,7 +1303,9 @@ namespace Barotrauma
{
if (Campaign == null) { return; }
if (!parent.Children.Any() || Submarine.MainSub != null && Submarine.MainSub != drawnSubmarine || GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
if (!parent.Children.Any() ||
Submarine.MainSub != null && Submarine.MainSub != drawnSubmarine ||
GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
{
GameMain.GameSession?.SubmarineInfo?.CheckSubsLeftBehind();
drawnSubmarine = Submarine.MainSub;
@@ -1312,6 +1323,10 @@ namespace Barotrauma
// we also need this when we first load in so we know which category entries to disable since the CampaignUI is created before the submarine is loaded in.
RefreshAll();
}
if (needsRefresh)
{
RefreshAll();
}
// accept an active confirmation popup if any
if (PlayerInput.KeyHit(Keys.Enter) && GUIMessageBox.MessageBoxes.Any())
@@ -1334,16 +1349,16 @@ namespace Barotrauma
{
if (GUI.MouseOn == frame)
{
if (HoveredItem != item) { CreateItemTooltip(item); }
HoveredItem = item;
if (HoveredEntity != item) { CreateItemTooltip(item); }
HoveredEntity = item;
if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradeTab == UpgradeTab.Upgrade && currentStoreLayout != null)
{
if (customizeTabOpen)
{
if (selectedUpgradeCategoryLayout != null)
{
var linkedItems = HoveredItem is Item ? Campaign.UpgradeManager.GetLinkedItemsToSwap((Item)HoveredItem) : new List<Item>();
if (selectedUpgradeCategoryLayout.FindChild(c => c.UserData as Item == HoveredItem || linkedItems.Contains(c.UserData as Item), recursive: true) is GUIButton itemElement)
var linkedItems = HoveredEntity is Item hoveredItem ? Campaign.UpgradeManager.GetLinkedItemsToSwap(hoveredItem) : new List<Item>();
if (selectedUpgradeCategoryLayout.FindChild(c => c.UserData is Item item && (item == HoveredEntity || linkedItems.Contains(item)), recursive: true) is GUIButton itemElement)
{
if (!itemElement.Selected) { itemElement.OnClicked(itemElement, itemElement.UserData); }
(itemElement.Parent?.Parent?.Parent as GUIListBox)?.ScrollToElement(itemElement);
@@ -1370,8 +1385,8 @@ namespace Barotrauma
// use pnpoly algorithm to detect if our mouse is within any of the hull polygons
if (subHullVertices.Any(hullVertex => ToolBox.PointIntersectsWithPolygon(PlayerInput.MousePosition, hullVertex)))
{
if (HoveredItem != firstStructure && !(firstStructure is null)) { CreateItemTooltip(firstStructure); }
HoveredItem = firstStructure;
if (HoveredEntity != firstStructure && !(firstStructure is null)) { CreateItemTooltip(firstStructure); }
HoveredEntity = firstStructure;
isMouseOnStructure = true;
GUI.MouseCursor = CursorState.Hand;
@@ -1382,7 +1397,7 @@ namespace Barotrauma
}
}
if (!isMouseOnStructure) { HoveredItem = null; }
if (!isMouseOnStructure) { HoveredEntity = null; }
}
// flip the tooltip if it is outside of the screen
@@ -1417,9 +1432,9 @@ namespace Barotrauma
*/
submarineInfoFrame = new GUILayoutGroup(rectT(0.25f, 0.2f, mainStoreLayout, Anchor.TopRight)) { IgnoreLayoutGroups = true };
// submarine name
new GUITextBlock(rectT(1, 0, submarineInfoFrame), submarine.Info.DisplayName, textAlignment: Alignment.Right, font: GUI.LargeFont);
new GUITextBlock(rectT(1, 0, submarineInfoFrame), submarine.Info.DisplayName, textAlignment: Alignment.Right, font: GUIStyle.LargeFont);
// submarine class
new GUITextBlock(rectT(1, 0, submarineInfoFrame), $"{TextManager.GetWithVariable("submarineclass.classsuffixformat", "[type]", TextManager.Get($"submarineclass.{submarine.Info.SubmarineClass}"))}", textAlignment: Alignment.Right, font: GUI.Font);
new GUITextBlock(rectT(1, 0, submarineInfoFrame), $"{TextManager.GetWithVariable("submarineclass.classsuffixformat", "[type]", TextManager.Get($"submarineclass.{submarine.Info.SubmarineClass}"))}", textAlignment: Alignment.Right, font: GUIStyle.Font);
var description = new GUITextBlock(rectT(1, 0, submarineInfoFrame), submarine.Info.Description, textAlignment: Alignment.Right, wrap: true);
submarineInfoFrame.RectTransform.ScreenSpaceOffset = new Point(0, (int)(16 * GUI.Scale));
@@ -1448,7 +1463,7 @@ namespace Barotrauma
Point size = new Point((int) (spriteSize * item.Scale / dockedBorders.Width * hullContainer.Rect.Width));
itemFrame = new GUIImage(rectT(size, component, Anchor.Center), icon, scaleToFit: true)
{
SelectedColor = GUI.Style.Orange,
SelectedColor = GUIStyle.Orange,
Color = previewWhite,
HoverCursor = CursorState.Hand,
SpriteEffects = item.Rotation > 90.0f && item.Rotation < 270.0f ? SpriteEffects.FlipVertically : SpriteEffects.None
@@ -1457,7 +1472,7 @@ namespace Barotrauma
{
new GUIImage(new RectTransform(new Vector2(0.8f), itemFrame.RectTransform, Anchor.TopLeft) { RelativeOffset = new Vector2(-0.2f) }, "WeaponSwitchIcon.DropShadow", scaleToFit: true)
{
SelectedColor = GUI.Style.Orange,
SelectedColor = GUIStyle.Orange,
Color = previewWhite,
CanBeFocused = false
};
@@ -1468,7 +1483,7 @@ namespace Barotrauma
Point size = new Point((int) (item.Rect.Width * item.Scale / dockedBorders.Width * hullContainer.Rect.Width), (int) (item.Rect.Height * item.Scale / dockedBorders.Height * hullContainer.Rect.Height));
itemFrame = new GUIFrame(rectT(size, component, Anchor.Center), style: "ScanLines")
{
SelectedColor = GUI.Style.Orange,
SelectedColor = GUIStyle.Orange,
OutlineColor = previewWhite,
Color = previewWhite,
OutlineThickness = 2,
@@ -1540,7 +1555,7 @@ namespace Barotrauma
// calculate the center point so we can draw a line from X to Y instead of drawing a rotated rectangle that is filled
Vector2 point1 = hullVertex[1] + (hullVertex[2] - hullVertex[1]) / 2;
Vector2 point2 = hullVertex[0] + (hullVertex[3] - hullVertex[0]) / 2;
GUI.DrawLine(spriteBatch, point1, point2, (highlightWalls ? GUI.Style.Orange * 0.6f : Color.DarkCyan * 0.3f), width: 10);
GUI.DrawLine(spriteBatch, point1, point2, (highlightWalls ? GUIStyle.Orange * 0.6f : Color.DarkCyan * 0.3f), width: 10);
if (GameMain.DebugDraw)
{
// the "collision box" is a bit bigger than the line we draw so this can be useful data (maybe)
@@ -1553,14 +1568,14 @@ namespace Barotrauma
{
int currentLevel = campaign.UpgradeManager.GetUpgradeLevel(prefab, category);
string progressText = TextManager.GetWithVariables("upgrades.progressformat", new[] { "[level]", "[maxlevel]" }, new[] { currentLevel.ToString(), prefab.MaxLevel.ToString() });
LocalizedString progressText = TextManager.GetWithVariables("upgrades.progressformat", ("[level]", currentLevel.ToString()), ("[maxlevel]", prefab.MaxLevel.ToString()));
if (prefabFrame.FindChild("progressbar", true) is { } progressParent)
{
GUIProgressBar bar = progressParent.GetChild<GUIProgressBar>();
if (bar != null)
{
bar.BarSize = currentLevel / (float) prefab.MaxLevel;
bar.Color = currentLevel >= prefab.MaxLevel ? GUI.Style.Green : GUI.Style.Orange;
bar.Color = currentLevel >= prefab.MaxLevel ? GUIStyle.Green : GUIStyle.Orange;
}
GUITextBlock block = progressParent.GetChild<GUITextBlock>();
@@ -1576,18 +1591,18 @@ namespace Barotrauma
if (priceLabel != null && !WaitForServerUpdate)
{
priceLabel.Text = FormatCurrency(price);
priceLabel.Text = TextManager.FormatCurrency(price);
if (currentLevel >= prefab.MaxLevel)
{
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
}
}
GUIButton button = buttonParent.GetChild<GUIButton>();
if (button != null)
{
button.Enabled = currentLevel < prefab.MaxLevel;
if (WaitForServerUpdate || !campaign.AllowedToManageCampaign() || price > campaign.Money)
if (WaitForServerUpdate || !campaign.Wallet.CanAfford(price))
{
button.Enabled = false;
}
@@ -1620,7 +1635,7 @@ namespace Barotrauma
else
{
parent.Enabled = false;
parent.SelectedColor = GUI.Style.Red * 0.5f;
parent.SelectedColor = GUIStyle.Red * 0.5f;
}
}
@@ -1632,12 +1647,12 @@ namespace Barotrauma
{
if (component.UserData != prefab) { continue; }
Dictionary<string, GUIComponentStyle> styles = GUI.Style.GetComponentStyle("upgradeindicator").ChildStyles;
Dictionary<Identifier, GUIComponentStyle> styles = GUIStyle.GetComponentStyle("upgradeindicator").ChildStyles;
if (!styles.ContainsKey("upgradeindicatoron") || !styles.ContainsKey("upgradeindicatordim") || !styles.ContainsKey("upgradeindicatoroff")) { continue; }
GUIComponentStyle onStyle = styles["upgradeindicatoron"];
GUIComponentStyle dimStyle = styles["upgradeindicatordim"];
GUIComponentStyle offStyle = styles["upgradeindicatoroff"];
GUIComponentStyle onStyle = styles["upgradeindicatoron".ToIdentifier()];
GUIComponentStyle dimStyle = styles["upgradeindicatordim".ToIdentifier()];
GUIComponentStyle offStyle = styles["upgradeindicatoroff".ToIdentifier()];
if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) >= prefab.MaxLevel)
{
@@ -1692,12 +1707,7 @@ namespace Barotrauma
return frames.ToArray();
}
private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign();
public static string FormatCurrency(int money, bool format = true)
{
return TextManager.GetWithVariable("CurrencyFormat", "[credits]", format ? string.Format(CultureInfo.InvariantCulture, "{0:N0}", money) : money.ToString());
}
private bool HasPermission => true;
// just a shortcut to create new RectTransforms since all the new RectTransform and new Vector2 confuses my IDE (and me)
private static RectTransform rectT(float x, float y, GUIComponent parentComponent, Anchor anchor = Anchor.TopLeft, ScaleBasis scaleBasis = ScaleBasis.Normal)

View File

@@ -34,23 +34,29 @@ namespace Barotrauma
public class TextSettings
{
public string Text;
public LocalizedString Text;
public int Width;
public TextSettings(Identifier textTag, int width)
{
Text = TextManager.GetFormatted(textTag);
Width = width;
}
public TextSettings(XElement element)
{
Text = TextManager.GetFormatted(element.GetAttributeString("text", string.Empty), true);
Text = TextManager.GetFormatted(element.GetAttributeIdentifier("text", Identifier.Empty));
Width = element.GetAttributeInt("width", 450);
}
}
public class VideoSettings
{
public string File;
public readonly string File;
public VideoSettings(XElement element)
public VideoSettings(string file)
{
File = element.GetAttributeString("file", string.Empty);
File = file;
}
}
@@ -75,13 +81,13 @@ namespace Barotrauma
}
videoView = new GUICustomComponent(new RectTransform(Point.Zero, videoFrame.RectTransform, Anchor.Center), (spriteBatch, guiCustomComponent) => { DrawVideo(spriteBatch, guiCustomComponent.Rect); });
title = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft), string.Empty, font: GUI.LargeFont, textColor: new Color(253, 174, 0), textAlignment: Alignment.Left);
title = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft), string.Empty, font: GUIStyle.LargeFont, textColor: new Color(253, 174, 0), textAlignment: Alignment.Left);
textContent = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft), string.Empty, font: GUI.Font, textAlignment: Alignment.TopLeft);
textContent = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft), string.Empty, font: GUIStyle.Font, textAlignment: Alignment.TopLeft);
objectiveTitle = new GUITextBlock(new RectTransform(new Vector2(1f, 0f), textFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter), string.Empty, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight, textColor: Color.White);
objectiveTitle = new GUITextBlock(new RectTransform(new Vector2(1f, 0f), textFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter), string.Empty, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight, textColor: Color.White);
objectiveTitle.Text = TextManager.Get("Tutorial.NewObjective");
objectiveText = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter), string.Empty, font: GUI.SubHeadingFont, textColor: new Color(4, 180, 108), textAlignment: Alignment.CenterRight);
objectiveText = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter), string.Empty, font: GUIStyle.SubHeadingFont, textColor: new Color(4, 180, 108), textAlignment: Alignment.CenterRight);
objectiveTitle.Visible = objectiveText.Visible = false;
}
@@ -120,7 +126,12 @@ namespace Barotrauma
background.AddToGUIUpdateList(ignoreChildren, order);
}
public void LoadContent(string contentPath, VideoSettings videoSettings, TextSettings textSettings, string contentId, bool startPlayback, string objective = "", Action callback = null)
public void LoadContent(string contentPath, VideoSettings videoSettings, TextSettings textSettings, Identifier contentId, bool startPlayback)
{
LoadContent(contentPath, videoSettings, textSettings, contentId, startPlayback, new RawLString(""), null);
}
public void LoadContent(string contentPath, VideoSettings videoSettings, TextSettings textSettings, Identifier contentId, bool startPlayback, LocalizedString objective, Action callback = null)
{
callbackOnStop = callback;
filePath = contentPath + videoSettings.File;
@@ -183,10 +194,10 @@ namespace Barotrauma
title.RectTransform.NonScaledSize = new Point(scaledTextWidth, scaledTitleHeight);
title.RectTransform.AbsoluteOffset = new Point((int)(5 * GUI.Scale), (int)(10 * GUI.Scale));
if (textSettings != null && !string.IsNullOrEmpty(textSettings.Text))
if (textSettings != null && !textSettings.Text.IsNullOrEmpty())
{
textSettings.Text = ToolBox.WrapText(textSettings.Text, scaledTextWidth, GUI.Font);
int wrappedHeight = textSettings.Text.Split('\n').Length * scaledTextHeight;
textSettings.Text = ToolBox.WrapText(textSettings.Text, scaledTextWidth, GUIStyle.Font);
int wrappedHeight = textSettings.Text.Value.Split('\n').Length * scaledTextHeight;
textFrame.RectTransform.NonScaledSize = new Point(scaledTextWidth + scaledBorderSize, wrappedHeight + scaledBorderSize + scaledButtonSize.Y + scaledTitleHeight);
@@ -203,7 +214,7 @@ namespace Barotrauma
textContent.RectTransform.AbsoluteOffset = new Point(0, scaledBorderSize + scaledTitleHeight);
}
if (!string.IsNullOrEmpty(objectiveText.Text))
if (!objectiveText.Text.IsNullOrEmpty())
{
int scaledXOffset = (int)(-10 * GUI.Scale);

View File

@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
@@ -19,32 +19,54 @@ namespace Barotrauma
private Func<int> getYesVotes, getNoVotes, getMaxVotes;
private bool votePassed;
private string votingOnText;
private List<RichTextData> votingOnTextData;
private RichString votingOnText;
private float votingTime = 100f;
private float timer;
private VoteType currentVoteType;
private Color submarineColor => GUI.Style.Orange;
private Color SubmarineColor => GUIStyle.Orange;
private Point createdForResolution;
public VotingInterface(Client starter, SubmarineInfo info, VoteType type, float votingTime)
public static VotingInterface CreateSubmarineVotingInterface(Client starter, SubmarineInfo info, VoteType type, float votingTime)
{
if (starter == null || info == null) return;
SetSubmarineVotingText(starter, info, type);
this.votingTime = votingTime;
getYesVotes = SubmarineYesVotes;
getNoVotes = SubmarineNoVotes;
getMaxVotes = SubmarineMaxVotes;
onVoteEnd = () => SendSubmarineVoteEndMessage(info, type);
if (starter == null || info == null) { return null; }
Initialize(starter, type);
var subVoting = new VotingInterface()
{
votingTime = votingTime,
getYesVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountYes(type) ?? 0,
getNoVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountNo(type) ?? 0,
getMaxVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountMax(type) ?? 0,
};
subVoting.onVoteEnd = () => subVoting.SendSubmarineVoteEndMessage(info, type);
subVoting.SetSubmarineVotingText(starter, info, type);
subVoting.Initialize(starter, type);
return subVoting;
}
public static VotingInterface CreateMoneyTransferVotingInterface(Client starter, Client from, Client to, int amount, float votingTime)
{
if (starter == null) { return null; }
if (from == null && to == null) { return null; }
var transferVoting = new VotingInterface()
{
votingTime = votingTime,
getYesVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountYes(VoteType.TransferMoney) ?? 0,
getNoVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountNo(VoteType.TransferMoney) ?? 0,
getMaxVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountMax(VoteType.TransferMoney) ?? 0,
};
transferVoting.onVoteEnd = () => transferVoting.SendMoneyTransferVoteEndMessage(from, to, amount);
transferVoting.SetMoneyTransferVotingText(starter, from, to, amount);
transferVoting.Initialize(starter, VoteType.TransferMoney);
return transferVoting;
}
private void Initialize(Client starter, VoteType type)
{
currentVoteType = type;
CreateVotingGUI();
if (starter.ID == GameMain.Client.ID) SetGUIToVotedState(2);
if (starter.ID == GameMain.Client.ID) { SetGUIToVotedState(2); }
VoteRunning = true;
}
@@ -52,7 +74,7 @@ namespace Barotrauma
{
createdForResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
if (frame != null) frame.Parent.RemoveChild(frame);
frame?.Parent.RemoveChild(frame);
frame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.VotingArea, GameMain.Client.InGameHUD.RectTransform), style: "");
int padding = HUDLayoutSettings.Padding * 2;
@@ -60,14 +82,14 @@ namespace Barotrauma
int yOffset = padding;
int paddedWidth = frame.Rect.Width - padding * 2;
votingTextBlock = new GUITextBlock(new RectTransform(new Point(paddedWidth, 0), frame.RectTransform), votingOnTextData, votingOnText, wrap: true);
votingTextBlock = new GUITextBlock(new RectTransform(new Point(paddedWidth, 0), frame.RectTransform), votingOnText, wrap: true);
votingTextBlock.RectTransform.NonScaledSize = votingTextBlock.RectTransform.MinSize = votingTextBlock.RectTransform.MaxSize = new Point(votingTextBlock.Rect.Width, votingTextBlock.Rect.Height);
votingTextBlock.RectTransform.IsFixedSize = true;
votingTextBlock.RectTransform.AbsoluteOffset = new Point(padding, yOffset);
yOffset += votingTextBlock.Rect.Height + spacing;
voteCounter = new GUITextBlock(new RectTransform(new Point(paddedWidth, 0), frame.RectTransform), "(0/0)", GUI.Style.Green, textAlignment: Alignment.Center);
voteCounter = new GUITextBlock(new RectTransform(new Point(paddedWidth, 0), frame.RectTransform), "(0/0)", GUIStyle.Green, textAlignment: Alignment.Center);
voteCounter.RectTransform.NonScaledSize = voteCounter.RectTransform.MinSize = voteCounter.RectTransform.MaxSize = new Point(voteCounter.Rect.Width, voteCounter.Rect.Height);
voteCounter.RectTransform.IsFixedSize = true;
voteCounter.RectTransform.AbsoluteOffset = new Point(padding, yOffset);
@@ -118,8 +140,8 @@ namespace Barotrauma
public void Update(float deltaTime)
{
if (!VoteRunning) return;
if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) CreateVotingGUI();
if (!VoteRunning) { return; }
if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) { CreateVotingGUI(); }
yesVotes = getYesVotes();
noVotes = getNoVotes();
maxVotes = getMaxVotes();
@@ -128,7 +150,6 @@ namespace Barotrauma
votingTimer.BarSize = timer / votingTime;
}
public void EndVote(bool passed, int yesVoteFinal, int noVoteFinal)
{
VoteRunning = false;
@@ -145,75 +166,94 @@ namespace Barotrauma
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
string characterRichString = $"‖color:{nameColor.R},{nameColor.G},{nameColor.B}‖{name}‖color:end‖";
string submarineRichString = $"‖color:{submarineColor.R},{submarineColor.G},{submarineColor.B}‖{info.DisplayName}‖color:end‖";
string submarineRichString = $"‖color:{SubmarineColor.R},{SubmarineColor.G},{SubmarineColor.B}‖{info.DisplayName}‖color:end‖";
LocalizedString text = string.Empty;
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
votingOnText = TextManager.GetWithVariables("submarinepurchaseandswitchvote", new string[] { "[playername]", "[submarinename]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, info.Price.ToString(), TextManager.Get("credit").ToLower() });
text = TextManager.GetWithVariables("submarinepurchaseandswitchvote",
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[amount]", info.Price.ToString()),
("[currencyname]", TextManager.Get("credit").ToLower()));
break;
case VoteType.PurchaseSub:
votingOnText = TextManager.GetWithVariables("submarinepurchasevote", new string[] { "[playername]", "[submarinename]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, info.Price.ToString(), TextManager.Get("credit").ToLower() });
text = TextManager.GetWithVariables("submarinepurchasevote",
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[amount]", info.Price.ToString()),
("[currencyname]", TextManager.Get("credit").ToLower()));
break;
case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
if (deliveryFee > 0)
{
votingOnText = TextManager.GetWithVariables("submarineswitchfeevote", new string[] { "[playername]", "[submarinename]", "[locationname]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, endLocation.Name, deliveryFee.ToString(), TextManager.Get("credit").ToLower() });
text = TextManager.GetWithVariables("submarineswitchfeevote",
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[locationname]", endLocation.Name),
("[amount]", deliveryFee.ToString()),
("[currencyname]", TextManager.Get("credit").ToLower()));
}
else
{
votingOnText = TextManager.GetWithVariables("submarineswitchnofeevote", new string[] { "[playername]", "[submarinename]" }, new string[] { characterRichString, submarineRichString });
text = TextManager.GetWithVariables("submarineswitchnofeevote",
("[playername]", characterRichString),
("[submarinename]", submarineRichString));
}
break;
}
votingOnTextData = RichTextData.GetRichTextData(votingOnText, out votingOnText);
}
private int SubmarineYesVotes()
{
return GameMain.NetworkMember.SubmarineVoteYesCount;
}
private int SubmarineNoVotes()
{
return GameMain.NetworkMember.SubmarineVoteNoCount;
}
private int SubmarineMaxVotes()
{
return GameMain.NetworkMember.SubmarineVoteMax;
votingOnText = RichString.Rich(text);
}
private void SendSubmarineVoteEndMessage(SubmarineInfo info, VoteType type)
{
GameMain.NetworkMember.AddChatMessage(GetSubmarineVoteResultMessage(info, type, yesVotes.ToString(), noVotes.ToString(), votePassed), ChatMessageType.Server);
GameMain.NetworkMember.AddChatMessage(GetSubmarineVoteResultMessage(info, type, yesVotes, noVotes, votePassed).Value, ChatMessageType.Server);
}
public static string GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, string yesVoteString, string noVoteString, bool votePassed)
private LocalizedString GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, int yesVoteCount, int noVoteCount, bool votePassed)
{
string result = string.Empty;
LocalizedString result = string.Empty;
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchaseandswitchvotepassed" : "submarinepurchaseandswitchvotefailed", new string[] { "[submarinename]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, info.Price.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
result = TextManager.GetWithVariables(votePassed ? "submarinepurchaseandswitchvotepassed" : "submarinepurchaseandswitchvotefailed",
("[submarinename]", info.DisplayName),
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", info.Price)),
("[currencyname]", TextManager.Get("credit").ToLower()),
("[yesvotecount]", yesVoteCount.ToString()),
("[novotecount]" , noVoteCount.ToString()));
break;
case VoteType.PurchaseSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchasevotepassed" : "submarinepurchasevotefailed", new string[] { "[submarinename]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, info.Price.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
result = TextManager.GetWithVariables(votePassed ? "submarinepurchasevotepassed" : "submarinepurchasevotefailed",
("[submarinename]", info.DisplayName),
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", info.Price)),
("[currencyname]", TextManager.Get("credit").ToLower()),
("[yesvotecount]", yesVoteCount.ToString()),
("[novotecount]", noVoteCount.ToString()));
break;
case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
if (deliveryFee > 0)
{
result = TextManager.GetWithVariables(votePassed ? "submarineswitchfeevotepassed" : "submarineswitchfeevotefailed", new string[] { "[submarinename]", "[locationname]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, endLocation.Name, deliveryFee.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
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", new string[] { "[submarinename]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, yesVoteString, noVoteString });
result = TextManager.GetWithVariables(votePassed ? "submarineswitchnofeevotepassed" : "submarineswitchnofeevotefailed",
("[submarinename]", info.DisplayName),
("[yesvotecount]", yesVoteCount.ToString()),
("[novotecount]", noVoteCount.ToString()));
}
break;
default:
@@ -223,6 +263,58 @@ namespace Barotrauma
}
#endregion
private void SetMoneyTransferVotingText(Client starter, Client from, Client to, int amount)
{
string name = starter.Name;
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
string characterRichString = $"‖color:{nameColor.R},{nameColor.G},{nameColor.B}‖{name}‖color:end‖";
LocalizedString text = string.Empty;
if (from == null && to != null)
{
text = TextManager.GetWithVariables("crewwallet.requestbanktoselfvote",
("[requester]", characterRichString),
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", amount)));
}
else if (from != null && to == null)
{
text = TextManager.GetWithVariables("crewwallet.requestselftobankvote",
("[requester]", characterRichString),
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", amount)));
}
else
{
//not supported atm: clients can only requests transfers between their own wallet and the bank
LocalizedString bankName = TextManager.Get("crewwallet.bank");
text = TextManager.GetWithVariables("crewwallet.requesttransfervote",
("[requester]", characterRichString),
("[player1]", from?.Character == null ? bankName : from.Character.Name),
("[player2]", to?.Character == null ? bankName : to.Character.Name),
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", amount)));
}
votingOnText = RichString.Rich(text);
}
private void SendMoneyTransferVoteEndMessage(Client from, Client to, int amount)
{
GameMain.NetworkMember.AddChatMessage(GetMoneyTransferVoteResultMessage(from, to, amount, yesVotes, noVotes, votePassed).Value, ChatMessageType.Server);
}
public static LocalizedString GetMoneyTransferVoteResultMessage(Client from, Client to, int transferAmount, int yesVoteCount, int noVoteCount, bool votePassed)
{
LocalizedString result = string.Empty;
if (from == null && to != null)
{
result = TextManager.GetWithVariables(votePassed ? "crewwallet.banktoplayer.votepassed" : "crewwallet.banktoplayer.votefailed",
("[playername]", to.Name),
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", transferAmount)),
("[yesvotecount]", yesVoteCount.ToString()),
("[novotecount]", noVoteCount.ToString()));
}
return result;
}
public void Remove()
{
if (frame != null)

View File

@@ -17,7 +17,7 @@ namespace Barotrauma
}
public Shape shape;
public string tooltip;
public LocalizedString tooltip;
public bool showTooltip = true;
public Rectangle DrawRect => new Rectangle((int)(DrawPos.X - (float)size / 2), (int)(DrawPos.Y - (float)size / 2), size, size);
public Rectangle InputRect
@@ -42,7 +42,7 @@ namespace Barotrauma
/// </summary>
public bool isFilled;
public int inputAreaMargin;
public Color color = GUI.Style.Red;
public Color color = GUIStyle.Red;
public Color? secondaryColor;
public Color textColor = Color.White;
public Color textBackgroundColor = Color.Black * 0.5f;
@@ -183,7 +183,7 @@ namespace Barotrauma
}
if (IsSelected)
{
if (showTooltip && !string.IsNullOrEmpty(tooltip))
if (showTooltip && !tooltip.IsNullOrEmpty())
{
var offset = tooltipOffset ?? new Vector2(size, -size / 2f);
GUI.DrawString(spriteBatch, DrawPos + offset, tooltip, textColor, textBackgroundColor);

View File

@@ -20,8 +20,8 @@ namespace Barotrauma
AbsoluteSpacing = GUI.IntScale(15)
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.Get("statisticsconsentheader"), font: GUI.SubHeadingFont, textColor: Color.White);
var mainText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.Get("statisticsconsenttext"), wrap: true, parseRichText: true);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.Get("statisticsconsentheader"), font: GUIStyle.SubHeadingFont, textColor: Color.White);
var mainText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), RichString.Rich(TextManager.Get("statisticsconsenttext")), wrap: true);
foreach (var data in mainText.RichTextData)
{
@@ -93,7 +93,7 @@ namespace Barotrauma
{
if (child is GUITextBlock textBlock)
{
textBlock.TextScale = MathHelper.Min(1.0f, 1.0f / GameSettings.TextScale);
textBlock.TextScale = MathHelper.Min(1.0f, 1.0f / GameSettings.CurrentConfig.Graphics.TextScale);
textBlock.RectTransform.MinSize = new Point(0, (int)textBlock.TextSize.Y);
textBlock.RectTransform.MaxSize = new Point(int.MaxValue, (int)textBlock.TextSize.Y);
}

View File

@@ -1,6 +1,9 @@
using Barotrauma.IO;
using Barotrauma.Media;
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Steam;
using Barotrauma.Transition;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
@@ -11,18 +14,13 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Barotrauma.IO;
using System.Threading;
using Barotrauma.Tutorials;
using Barotrauma.Media;
using Barotrauma.Extensions;
using System.Threading.Tasks;
namespace Barotrauma
{
class GameMain : Game
{
public static LuaSetup Lua;
public static LuaCsSetup LuaCs;
public static bool ShowFPS = false;
public static bool ShowPerf = false;
@@ -47,8 +45,17 @@ namespace Barotrauma
public static MainMenuScreen MainMenuScreen;
public static NetLobbyScreen NetLobbyScreen;
public static ModDownloadScreen ModDownloadScreen;
public static void ResetNetLobbyScreen()
{
NetLobbyScreen?.Release();
NetLobbyScreen = new NetLobbyScreen();
ModDownloadScreen?.Release();
ModDownloadScreen = new ModDownloadScreen();
}
public static ServerListScreen ServerListScreen;
public static SteamWorkshopScreen SteamWorkshopScreen;
public static SubEditorScreen SubEditorScreen;
public static TestScreen TestScreen;
@@ -66,19 +73,7 @@ namespace Barotrauma
public static Thread MainThread { get; private set; }
private static ContentPackage vanillaContent;
public static ContentPackage VanillaContent
{
get
{
if (vanillaContent == null)
{
// TODO: Dynamic method for defining and finding the vanilla content package.
vanillaContent = ContentPackage.CorePackages.SingleOrDefault(cp => Path.GetFileName(cp.Path).Equals("vanilla 0.9.xml", StringComparison.OrdinalIgnoreCase));
}
return vanillaContent;
}
}
public static ContentPackage VanillaContent => ContentPackageManager.VanillaCorePackage;
private static GameSession gameSession;
public static GameSession GameSession
@@ -96,7 +91,6 @@ namespace Barotrauma
}
public static ParticleManager ParticleManager;
public static DecalManager DecalManager;
private static World world;
public static World World
@@ -112,10 +106,8 @@ namespace Barotrauma
public static LoadingScreen TitleScreen;
private bool loadingScreenOpen;
public static GameSettings Config;
private CoroutineHandle loadingCoroutine;
private bool hasLoaded;
public bool HasLoaded { get; private set; }
private readonly GameTime fixedTime;
@@ -234,11 +226,11 @@ namespace Barotrauma
throw new Exception("Content folder not found. If you are trying to compile the game from the source code and own a legal copy of the game, you can copy the Content folder from the game's files to BarotraumaShared/Content.");
}
Config = new GameSettings();
LuaCs = new LuaCsSetup();
Lua = new LuaSetup();
Md5Hash.LoadCache();
GameSettings.Init();
Md5Hash.Cache.Load();
ConsoleArguments = args;
@@ -294,24 +286,42 @@ namespace Barotrauma
public void ApplyGraphicsSettings()
{
GraphicsWidth = Config.GraphicsWidth;
GraphicsHeight = Config.GraphicsHeight;
switch (Config.WindowMode)
void updateConfig()
{
var config = GameSettings.CurrentConfig;
config.Graphics.Width = GraphicsWidth;
config.Graphics.Height = GraphicsHeight;
GameSettings.SetCurrentConfig(config);
}
GraphicsWidth = GameSettings.CurrentConfig.Graphics.Width;
GraphicsHeight = GameSettings.CurrentConfig.Graphics.Height;
if (GraphicsWidth <= 0 || GraphicsHeight <= 0)
{
GraphicsWidth = GraphicsDevice.DisplayMode.Width;
GraphicsHeight = GraphicsDevice.DisplayMode.Height;
updateConfig();
}
switch (GameSettings.CurrentConfig.Graphics.DisplayMode)
{
case WindowMode.BorderlessWindowed:
GraphicsWidth = GraphicsDevice.DisplayMode.Width;
GraphicsHeight = GraphicsDevice.DisplayMode.Height;
updateConfig();
break;
case WindowMode.Windowed:
GraphicsWidth = Math.Min(GraphicsDevice.DisplayMode.Width, GraphicsWidth);
GraphicsHeight = Math.Min(GraphicsDevice.DisplayMode.Height, GraphicsHeight);
updateConfig();
break;
}
GraphicsDeviceManager.GraphicsProfile = GfxProfile;
GraphicsDeviceManager.PreferredBackBufferFormat = SurfaceFormat.Color;
GraphicsDeviceManager.PreferMultiSampling = false;
GraphicsDeviceManager.SynchronizeWithVerticalRetrace = Config.VSyncEnabled;
SetWindowMode(Config.WindowMode);
GraphicsDeviceManager.SynchronizeWithVerticalRetrace = GameSettings.CurrentConfig.Graphics.VSync;
SetWindowMode(GameSettings.CurrentConfig.Graphics.DisplayMode);
defaultViewport = GraphicsDevice.Viewport;
@@ -321,8 +331,8 @@ namespace Barotrauma
public void SetWindowMode(WindowMode windowMode)
{
WindowMode = windowMode;
GraphicsDeviceManager.HardwareModeSwitch = Config.WindowMode != WindowMode.BorderlessWindowed;
GraphicsDeviceManager.IsFullScreen = Config.WindowMode == WindowMode.Fullscreen || Config.WindowMode == WindowMode.BorderlessWindowed;
GraphicsDeviceManager.HardwareModeSwitch = windowMode != WindowMode.BorderlessWindowed;
GraphicsDeviceManager.IsFullScreen = windowMode == WindowMode.Fullscreen || windowMode == WindowMode.BorderlessWindowed;
Window.IsBorderless = !GraphicsDeviceManager.HardwareModeSwitch;
GraphicsDeviceManager.PreferredBackBufferWidth = GraphicsWidth;
@@ -394,7 +404,7 @@ namespace Barotrauma
loadingScreenOpen = true;
TitleScreen = new LoadingScreen(GraphicsDevice)
{
WaitForLanguageSelection = Config.ShowLanguageSelectionPrompt
WaitForLanguageSelection = GameSettings.CurrentConfig.Language == LanguageIdentifier.None
};
bool canLoadInSeparateThread = true;
@@ -411,27 +421,31 @@ namespace Barotrauma
private IEnumerable<CoroutineStatus> Load(bool isSeparateThread)
{
if (GameSettings.VerboseLogging)
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.NewMessage("LOADING COROUTINE", Color.Lime);
}
while (TitleScreen.WaitForLanguageSelection)
ContentPackageManager.LoadVanillaFileList();
if (TitleScreen.WaitForLanguageSelection)
{
yield return CoroutineStatus.Running;
ContentPackageManager.VanillaCorePackage.LoadFilesOfType<TextFile>();
TitleScreen.AvailableLanguages = TextManager.AvailableLanguages.OrderBy(l => l.Value != "english".ToIdentifier()).ThenBy(l => l.Value).ToArray();
while (TitleScreen.WaitForLanguageSelection)
{
yield return CoroutineStatus.Running;
}
ContentPackageManager.VanillaCorePackage.UnloadFilesOfType<TextFile>();
}
SoundManager = new Sounds.SoundManager();
SoundManager.SetCategoryGainMultiplier("default", Config.SoundVolume, 0);
SoundManager.SetCategoryGainMultiplier("ui", Config.SoundVolume, 0);
SoundManager.SetCategoryGainMultiplier("waterambience", Config.SoundVolume, 0);
SoundManager.SetCategoryGainMultiplier("music", Config.MusicVolume, 0);
SoundManager.SetCategoryGainMultiplier("voip", Math.Min(Config.VoiceChatVolume, 1.0f), 0);
SoundManager.ApplySettings();
if (Config.EnableSplashScreen && !ConsoleArguments.Contains("-skipintro"))
if (GameSettings.CurrentConfig.EnableSplashScreen && !ConsoleArguments.Contains("-skipintro"))
{
var pendingSplashScreens = TitleScreen.PendingSplashScreens;
float baseVolume = MathHelper.Clamp(Config.SoundVolume * 2.0f, 0.0f, 1.0f);
float baseVolume = MathHelper.Clamp(GameSettings.CurrentConfig.Audio.SoundVolume * 2.0f, 0.0f, 1.0f);
pendingSplashScreens?.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_UTG.webm", baseVolume * 0.5f));
pendingSplashScreens?.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_FF.webm", baseVolume));
pendingSplashScreens?.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_Daedalic.webm", baseVolume * 0.1f));
@@ -447,153 +461,57 @@ namespace Barotrauma
}
}
GUI.Init(Window, Config.AllEnabledPackages, GraphicsDevice);
GUI.Init();
yield return CoroutineStatus.Running;
UgcTransition.Prepare();
var contentPackageLoadRoutine = ContentPackageManager.Init();
foreach (var progress in contentPackageLoadRoutine)
{
const float min = 1f, max = 70f;
TitleScreen.LoadState = MathHelper.Lerp(min, max, progress.Value);
yield return CoroutineStatus.Running;
}
TextManager.VerifyLanguageAvailable();
DebugConsole.Init();
if (Config.AutoUpdateWorkshopItems)
{
Config.WaitingForAutoUpdate = true;
TaskPool.Add("AutoUpdateWorkshopItemsAsync",
SteamManager.AutoUpdateWorkshopItemsAsync(), (task) =>
{
if (!task.TryGetResult(out bool result)) { return; }
Config.WaitingForAutoUpdate = false;
});
while (Config.WaitingForAutoUpdate) { yield return CoroutineStatus.Running; }
}
#if DEBUG
if (Config.ModBreakerMode)
{
Config.SelectCorePackage(ContentPackage.CorePackages.GetRandom());
foreach (var regularPackage in ContentPackage.RegularPackages)
{
if (Rand.Range(0.0, 1.0) <= 0.5)
{
Config.EnableRegularPackage(regularPackage);
}
else
{
Config.DisableRegularPackage(regularPackage);
}
}
ContentPackage.SortContentPackages(p =>
{
return Rand.Int(int.MaxValue);
});
}
#endif
if (Config.AllEnabledPackages.None())
{
DebugConsole.Log("No content packages selected");
}
else
{
DebugConsole.Log("Selected content packages: " + string.Join(", ", Config.AllEnabledPackages.Select(cp => cp.Name)));
}
ContentPackageManager.LogEnabledRegularPackageErrors();
#if !DEBUG && !OSX
GameAnalyticsManager.InitIfConsented();
#endif
yield return CoroutineStatus.Running;
Debug.WriteLine("sounds");
int i = 0;
foreach (CoroutineStatus status in SoundPlayer.Init())
{
if (status == CoroutineStatus.Success) break;
i++;
TitleScreen.LoadState = SoundPlayer.SoundCount == 0 ?
1.0f :
Math.Min(40.0f * i / Math.Max(SoundPlayer.SoundCount, 1), 40.0f);
yield return CoroutineStatus.Running;
}
TitleScreen.LoadState = 40.0f;
yield return CoroutineStatus.Running;
LightManager = new Lights.LightManager(base.GraphicsDevice, Content);
TitleScreen.LoadState = 41.0f;
yield return CoroutineStatus.Running;
GUI.LoadContent();
TitleScreen.LoadState = 42.0f;
yield return CoroutineStatus.Running;
TaskPool.Add("InitRelayNetworkAccess", SteamManager.InitRelayNetworkAccess(), (t) => { });
FactionPrefab.LoadFactions();
NPCSet.LoadSets();
CharacterPrefab.LoadAll();
MissionPrefab.Init();
TraitorMissionPrefab.Init();
MapEntityPrefab.Init();
Tutorials.Tutorial.Init();
MapGenerationParams.Init();
LevelGenerationParams.LoadPresets();
CaveGenerationParams.LoadPresets();
OutpostGenerationParams.LoadPresets();
WreckAIConfig.LoadAll();
EventSet.LoadPrefabs();
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
TalentPrefab.LoadAll(GetFilesOfType(ContentType.Talents));
TalentTree.LoadAll(GetFilesOfType(ContentType.TalentTrees));
Order.Init();
EventManagerSettings.Init();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
HintManager.Init();
TitleScreen.LoadState = 50.0f;
yield return CoroutineStatus.Running;
StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure));
TitleScreen.LoadState = 55.0f;
yield return CoroutineStatus.Running;
UpgradePrefab.LoadAll(GetFilesOfType(ContentType.UpgradeModules));
TitleScreen.LoadState = 56.0f;
yield return CoroutineStatus.Running;
JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs));
CorpsePrefab.LoadAll(GetFilesOfType(ContentType.Corpses));
NPCConversation.LoadAll(GetFilesOfType(ContentType.NPCConversations));
ItemAssemblyPrefab.LoadAll();
TitleScreen.LoadState = 60.0f;
yield return CoroutineStatus.Running;
CoreEntityPrefab.InitCorePrefabs();
GameModePreset.Init();
SaveUtil.DeleteDownloadedSubs();
SubmarineInfo.RefreshSavedSubs();
TitleScreen.LoadState = 65.0f;
TitleScreen.LoadState = 75.0f;
yield return CoroutineStatus.Running;
GameScreen = new GameScreen(GraphicsDeviceManager.GraphicsDevice, Content);
TitleScreen.LoadState = 68.0f;
ParticleManager = new ParticleManager(GameScreen.Cam);
LightManager = new Lights.LightManager(base.GraphicsDevice, Content);
TitleScreen.LoadState = 80.0f;
yield return CoroutineStatus.Running;
MainMenuScreen = new MainMenuScreen(this);
ServerListScreen = new ServerListScreen();
TitleScreen.LoadState = 70.0f;
TitleScreen.LoadState = 85.0f;
yield return CoroutineStatus.Running;
#if USE_STEAM
SteamWorkshopScreen = new SteamWorkshopScreen();
if (SteamManager.IsInitialized)
{
Steamworks.SteamFriends.OnGameRichPresenceJoinRequested += OnInvitedToGame;
@@ -603,25 +521,25 @@ namespace Barotrauma
{
//check the achievements too, so we don't consider people who've played the game before this "gamelaunchcount" stat was added as being 1st-time-players
//(people who have played previous versions, but not unlocked any achievements, will be incorrectly considered 1st-time-players, but that should be a small enough group to not skew the statistics)
if (!achievements.Any() && SteamManager.GetStatInt("gamelaunchcount") <= 0)
if (!achievements.Any() && SteamManager.GetStatInt("gamelaunchcount".ToIdentifier()) <= 0)
{
IsFirstLaunch = true;
GameAnalyticsManager.AddDesignEvent("FirstLaunch");
}
}
SteamManager.IncrementStat("gamelaunchcount", 1);
SteamManager.IncrementStat("gamelaunchcount".ToIdentifier(), 1);
}
#endif
SubEditorScreen = new SubEditorScreen();
TestScreen = new TestScreen();
TitleScreen.LoadState = 75.0f;
TitleScreen.LoadState = 90.0f;
yield return CoroutineStatus.Running;
ParticleEditorScreen = new ParticleEditorScreen();
TitleScreen.LoadState = 80.0f;
TitleScreen.LoadState = 95.0f;
yield return CoroutineStatus.Running;
LevelEditorScreen = new LevelEditorScreen();
@@ -632,31 +550,25 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
TitleScreen.LoadState = 85.0f;
ParticleManager = new ParticleManager(GameScreen.Cam);
ParticleManager.LoadPrefabs();
TitleScreen.LoadState = 88.0f;
LevelObjectPrefab.LoadAll();
TitleScreen.LoadState = 90.0f;
yield return CoroutineStatus.Running;
DecalManager = new DecalManager();
LocationType.Init();
MainMenuScreen.Select();
foreach (string steamError in SteamManager.InitializationErrors)
foreach (Identifier steamError in SteamManager.InitializationErrors)
{
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get(steamError));
}
TitleScreen.LoadState = 100.0f;
hasLoaded = true;
if (GameSettings.VerboseLogging)
HasLoaded = true;
if (GameSettings.CurrentConfig.VerboseLogging)
{
DebugConsole.NewMessage("LOADING COROUTINE FINISHED", Color.Lime);
}
yield return CoroutineStatus.Success;
#if CLIENT
LuaCsUpdateChecker.Check();
#endif
yield return CoroutineStatus.Success;
}
@@ -674,23 +586,6 @@ namespace Barotrauma
MainThread = null;
}
/// <summary>
/// Returns the file paths of all files of the given type in the content packages.
/// </summary>
/// <param name="type"></param>
/// <param name="searchAllContentPackages">If true, also returns files in content packages that are installed but not currently selected.</param>
public IEnumerable<ContentFile> GetFilesOfType(ContentType type, bool searchAllContentPackages = false)
{
if (searchAllContentPackages)
{
return ContentPackage.GetFilesOfType(ContentPackage.AllPackages, type);
}
else
{
return ContentPackage.GetFilesOfType(Config.AllEnabledPackages, type);
}
}
public void OnInvitedToGame(Steamworks.Friend friend, string connectCommand) => OnInvitedToGame(connectCommand);
public void OnInvitedToGame(string connectCommand)
@@ -741,7 +636,7 @@ namespace Barotrauma
if (SoundManager != null)
{
if (WindowActive || !Config.MuteOnFocusLost)
if (WindowActive || !GameSettings.CurrentConfig.Audio.MuteOnFocusLost)
{
SoundManager.ListenerGain = SoundManager.CompressionDynamicRangeGain;
}
@@ -790,29 +685,30 @@ namespace Barotrauma
CancelQuickStart = !CancelQuickStart;
}
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled || Config.TestScreenEnabled) && FirstLoad && !CancelQuickStart)
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen &&
(GameSettings.CurrentConfig.AutomaticQuickStartEnabled ||
GameSettings.CurrentConfig.AutomaticCampaignLoadEnabled ||
GameSettings.CurrentConfig.TestScreenEnabled) && FirstLoad && !CancelQuickStart)
{
loadingScreenOpen = false;
FirstLoad = false;
if (Config.TestScreenEnabled)
if (GameSettings.CurrentConfig.TestScreenEnabled)
{
TestScreen.Select();
}
else if (Config.AutomaticQuickStartEnabled)
}
else if (GameSettings.CurrentConfig.AutomaticQuickStartEnabled)
{
MainMenuScreen.QuickStart();
}
else if (Config.AutomaticCampaignLoadEnabled)
else if (GameSettings.CurrentConfig.AutomaticCampaignLoadEnabled)
{
IEnumerable<string> saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
var saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
if (saveFiles.Count() > 0)
{
saveFiles = saveFiles.OrderBy(file => File.GetLastWriteTime(file));
try
{
SaveUtil.LoadGame(saveFiles.Last());
SaveUtil.LoadGame(saveFiles.OrderBy(file => file.SaveTime).Last().FilePath);
}
catch (Exception e)
{
@@ -826,12 +722,12 @@ namespace Barotrauma
NetworkMember?.Update((float)Timing.Step);
if (!hasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine))
if (!HasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine))
{
throw new LoadingException(loadingCoroutine.Exception);
}
}
else if (hasLoaded)
else if (HasLoaded)
{
if (ConnectLobby != 0)
{
@@ -858,7 +754,7 @@ namespace Barotrauma
GameMain.MainMenuScreen.Select();
}
UInt64 serverSteamId = SteamManager.SteamIDStringToUInt64(ConnectEndpoint);
Client = new GameClient(Config.PlayerName,
Client = new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(SteamManager.GetUsername()),
serverSteamId != 0 ? null : ConnectEndpoint,
serverSteamId,
string.IsNullOrWhiteSpace(ConnectName) ? ConnectEndpoint : ConnectName);
@@ -892,9 +788,9 @@ namespace Barotrauma
{
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
}
else if (Tutorial.Initialized && Tutorial.ContentRunning)
else if (GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial.ContentRunning)
{
(GameSession.GameMode as TutorialMode).Tutorial.CloseActiveContentGUI();
tutorialMode.Tutorial.CloseActiveContentGUI();
}
else if (GameSession.IsTabMenuOpen)
{
@@ -939,8 +835,11 @@ namespace Barotrauma
#endif
GUI.ClearUpdateList();
Paused = (DebugConsole.IsOpen || GUI.PauseMenuOpen || GUI.SettingsMenuOpen || Tutorial.ContentRunning || DebugConsole.Paused) &&
(NetworkMember == null || !NetworkMember.GameStarted);
Paused =
(DebugConsole.IsOpen || DebugConsole.Paused ||
GUI.PauseMenuOpen || GUI.SettingsMenuOpen ||
(GameSession?.GameMode is TutorialMode tutoMode && tutoMode.Tutorial.ContentRunning)) &&
(NetworkMember == null || !NetworkMember.GameStarted);
if (GameSession?.GameMode != null && GameSession.GameMode.Paused)
{
Paused = true;
@@ -948,7 +847,7 @@ namespace Barotrauma
}
#if !DEBUG
if (NetworkMember == null && !WindowActive && !Paused && true && Config.PauseOnFocusLost &&
if (NetworkMember == null && !WindowActive && !Paused && true && GameSettings.CurrentConfig.PauseOnFocusLost &&
Screen.Selected != MainMenuScreen && Screen.Selected != ServerListScreen && Screen.Selected != NetLobbyScreen &&
Screen.Selected != SubEditorScreen && Screen.Selected != LevelEditorScreen)
{
@@ -973,9 +872,9 @@ namespace Barotrauma
{
Screen.Selected.Update(Timing.Step);
}
else if (Tutorial.Initialized && Tutorial.ContentRunning)
else if (GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial.ContentRunning)
{
(GameSession.GameMode as TutorialMode).Update((float)Timing.Step);
tutorialMode.Update((float)Timing.Step);
}
else
{
@@ -992,6 +891,27 @@ namespace Barotrauma
NetworkMember?.Update((float)Timing.Step);
GUI.Update((float)Timing.Step);
#if DEBUG
if (DebugDraw && GUI.MouseOn != null && PlayerInput.IsCtrlDown() && PlayerInput.KeyHit(Keys.G))
{
List<GUIComponent> hierarchy = new List<GUIComponent>();
var currComponent = GUI.MouseOn;
while (currComponent != null)
{
hierarchy.Add(currComponent);
currComponent = currComponent.Parent;
}
DebugConsole.NewMessage("*********************");
foreach (var component in hierarchy)
{
if (component is { MouseRect: var mouseRect, Rect: var rect })
{
DebugConsole.NewMessage($"{component.GetType().Name} {component.Style?.Name ?? "[null]"} {rect.Bottom} {mouseRect.Bottom}", mouseRect!=rect ? Color.Lime : Color.Red);
}
}
}
#endif
}
CoroutineManager.Update((float)Timing.Step, Paused ? 0.0f : (float)Timing.Step);
@@ -1002,8 +922,8 @@ namespace Barotrauma
SoundManager?.Update();
GameMain.Lua.Update();
GameMain.Lua.hook.Call("think", new object[] { });
GameMain.LuaCs.Update();
GameMain.LuaCs.Hook.Call("think");
Timing.Accumulator -= Timing.Step;
@@ -1042,7 +962,7 @@ namespace Barotrauma
if (Timing.FrameLimit > 0)
{
double step = 1.0 / Timing.FrameLimit;
while (!Config.VSyncEnabled && sw.Elapsed.TotalSeconds + deltaTime < step)
while (!GameSettings.CurrentConfig.Graphics.VSync && sw.Elapsed.TotalSeconds + deltaTime < step)
{
Thread.Sleep(1);
}
@@ -1054,7 +974,7 @@ namespace Barotrauma
{
TitleScreen.Draw(spriteBatch, base.GraphicsDevice, (float)deltaTime);
}
else if (hasLoaded)
else if (HasLoaded)
{
Screen.Selected.Draw(deltaTime, base.GraphicsDevice, spriteBatch);
}
@@ -1062,8 +982,33 @@ namespace Barotrauma
if (DebugDraw && GUI.MouseOn != null)
{
spriteBatch.Begin();
GUI.DrawRectangle(spriteBatch, GUI.MouseOn.MouseRect, Color.Lime);
GUI.DrawRectangle(spriteBatch, GUI.MouseOn.Rect, Color.Cyan);
if (PlayerInput.IsCtrlDown() && PlayerInput.KeyDown(Keys.G))
{
List<GUIComponent> hierarchy = new List<GUIComponent>();
var currComponent = GUI.MouseOn;
while (currComponent != null)
{
hierarchy.Add(currComponent);
currComponent = currComponent.Parent;
}
Color[] colors = { Color.Lime, Color.Yellow, Color.Aqua, Color.Red };
for (int index = 0; index < hierarchy.Count; index++)
{
var component = hierarchy[index];
if (component is { MouseRect: var mouseRect, Rect: var rect })
{
if (mouseRect.IsEmpty) { mouseRect = rect; }
mouseRect.Location += (index%2,(index%4)/2);
GUI.DrawRectangle(spriteBatch, mouseRect, colors[index%4]);
}
}
}
else
{
GUI.DrawRectangle(spriteBatch, GUI.MouseOn.MouseRect, Color.Lime);
GUI.DrawRectangle(spriteBatch, GUI.MouseOn.Rect, Color.Cyan);
}
spriteBatch.End();
}
@@ -1078,7 +1023,7 @@ namespace Barotrauma
if (showVerificationPrompt)
{
string text = (Screen.Selected is CharacterEditor.CharacterEditorScreen || Screen.Selected is SubEditorScreen) ? "PauseMenuQuitVerificationEditor" : "PauseMenuQuitVerification";
var msgBox = new GUIMessageBox("", TextManager.Get(text), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
var msgBox = new GUIMessageBox("", TextManager.Get(text), new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
{
UserData = "verificationprompt"
};
@@ -1106,9 +1051,9 @@ namespace Barotrauma
// Update store stock when saving and quitting in an outpost (normally updated when CampaignMode.End() is called)
if (GameSession?.Campaign is SinglePlayerCampaign spCampaign && Level.IsLoadedOutpost && spCampaign.Map?.CurrentLocation != null && spCampaign.CargoManager != null)
{
spCampaign.Map.CurrentLocation.AddToStock(spCampaign.CargoManager.SoldItems);
spCampaign.Map.CurrentLocation.AddStock(spCampaign.CargoManager.SoldItems);
spCampaign.CargoManager.ClearSoldItemsProjSpecific();
spCampaign.Map.CurrentLocation.RemoveFromStock(spCampaign.CargoManager.PurchasedItems);
spCampaign.Map.CurrentLocation.RemoveStock(spCampaign.CargoManager.PurchasedItems);
}
SaveUtil.SaveGame(GameSession.SavePath);
@@ -1126,31 +1071,31 @@ namespace Barotrauma
{
double roundDuration = Timing.TotalTime - GameSession.RoundStartTime;
GameAnalyticsManager.AddProgressionEvent(GameAnalyticsManager.ProgressionStatus.Fail,
GameSession.GameMode?.Preset.Identifier ?? "none",
GameSession.GameMode?.Preset.Identifier.Value ?? "none",
roundDuration);
string eventId = "QuitRound:" + (GameSession.GameMode?.Preset.Identifier ?? "none") + ":";
string eventId = "QuitRound:" + (GameSession.GameMode?.Preset.Identifier.Value ?? "none") + ":";
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:CurrentIntensity", GameSession.EventManager.CurrentIntensity);
foreach (var activeEvent in GameSession.EventManager.ActiveEvents)
{
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:ActiveEvents:" + activeEvent.ToString());
}
GameSession.LogEndRoundStats(eventId);
if (Tutorial.Initialized)
if (GameSession.GameMode is TutorialMode tutorialMode)
{
((TutorialMode)GameSession.GameMode).Tutorial?.Stop();
tutorialMode.Tutorial?.Stop();
}
}
GUIMessageBox.CloseAll();
MainMenuScreen.Select();
GameSession = null;
GameMain.Lua.Stop();
GameMain.LuaCs.Stop();
}
public void ShowCampaignDisclaimer(Action onContinue = null)
{
var msgBox = new GUIMessageBox(TextManager.Get("CampaignDisclaimerTitle"), TextManager.Get("CampaignDisclaimerText"),
new string[] { TextManager.Get("CampaignRoadMapTitle"), TextManager.Get("OK") });
new LocalizedString[] { TextManager.Get("CampaignRoadMapTitle"), TextManager.Get("OK") });
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
{
@@ -1161,8 +1106,10 @@ namespace Barotrauma
msgBox.Buttons[1].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += (_, __) => { onContinue?.Invoke(); return true; };
Config.CampaignDisclaimerShown = true;
Config.SaveNewPlayerConfig();
var config = GameSettings.CurrentConfig;
config.CampaignDisclaimerShown = true;
GameSettings.SetCurrentConfig(config);
GameSettings.SaveCurrentConfig();
}
public void ShowEditorDisclaimer()
@@ -1170,16 +1117,16 @@ namespace Barotrauma
var msgBox = new GUIMessageBox(TextManager.Get("EditorDisclaimerTitle"), TextManager.Get("EditorDisclaimerText"));
var linkHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), msgBox.Content.RectTransform)) { Stretch = true, RelativeSpacing = 0.025f };
linkHolder.RectTransform.MaxSize = new Point(int.MaxValue, linkHolder.Rect.Height);
List<Pair<string, string>> links = new List<Pair<string, string>>()
List<(LocalizedString Caption, LocalizedString Url)> links = new List<(LocalizedString, LocalizedString)>()
{
new Pair<string, string>(TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl")),
new Pair<string, string>(TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl")),
(TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl")),
(TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl")),
};
foreach (var link in links)
{
new GUIButton(new RectTransform(new Vector2(1.0f, 0.2f), linkHolder.RectTransform), link.First, style: "MainMenuGUIButton", textAlignment: Alignment.Left)
new GUIButton(new RectTransform(new Vector2(1.0f, 0.2f), linkHolder.RectTransform), link.Caption, style: "MainMenuGUIButton", textAlignment: Alignment.Left)
{
UserData = link.Second,
UserData = link.Url,
OnClicked = (btn, userdata) =>
{
ShowOpenUrlInWebBrowserPrompt(userdata as string);
@@ -1190,8 +1137,10 @@ namespace Barotrauma
msgBox.InnerFrame.RectTransform.MinSize = new Point(0,
msgBox.InnerFrame.Rect.Height + linkHolder.Rect.Height + msgBox.Content.AbsoluteSpacing * 2 + 10);
Config.EditorDisclaimerShown = true;
Config.SaveNewPlayerConfig();
var config = GameSettings.CurrentConfig;
config.EditorDisclaimerShown = true;
GameSettings.SetCurrentConfig(config);
GameSettings.SaveCurrentConfig();
}
public void ShowBugReporter()
@@ -1265,7 +1214,8 @@ namespace Barotrauma
}
if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.ShutDown(); }
if (GameSettings.SaveDebugConsoleLogs || GameSettings.VerboseLogging) { DebugConsole.SaveLogs(); }
if (GameSettings.CurrentConfig.SaveDebugConsoleLogs
|| GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.SaveLogs(); }
base.OnExiting(sender, args);
}
@@ -1275,14 +1225,14 @@ namespace Barotrauma
if (string.IsNullOrEmpty(url)) { return; }
if (GUIMessageBox.VisibleBox?.UserData as string == "verificationprompt") { return; }
string text = TextManager.GetWithVariable("openlinkinbrowserprompt", "[link]", url);
string extensionText = TextManager.Get(promptExtensionTag, returnNull: true, useEnglishAsFallBack: false);
if (!string.IsNullOrEmpty(extensionText))
LocalizedString text = TextManager.GetWithVariable("openlinkinbrowserprompt", "[link]", url);
LocalizedString extensionText = TextManager.Get(promptExtensionTag);
if (!extensionText.IsNullOrEmpty())
{
text += $"\n\n{extensionText}";
}
var msgBox = new GUIMessageBox("", text, new string[] { TextManager.Get("Yes"), TextManager.Get("No") })
var msgBox = new GUIMessageBox("", text, new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") })
{
UserData = "verificationprompt"
};

View File

@@ -45,28 +45,39 @@ namespace Barotrauma
return SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
}
public void SetItemsInBuyCrate(List<PurchasedItem> items)
public void SetItemsInBuyCrate(Dictionary<Identifier, List<PurchasedItem>> items)
{
ItemsInBuyCrate.Clear();
ItemsInBuyCrate.AddRange(items);
foreach (var entry in items)
{
ItemsInBuyCrate.Add(entry.Key, entry.Value);
}
OnItemsInBuyCrateChanged?.Invoke();
}
public void SetItemsInSubSellCrate(List<PurchasedItem> items)
public void SetItemsInSubSellCrate(Dictionary<Identifier, List<PurchasedItem>> items)
{
ItemsInSellFromSubCrate.Clear();
ItemsInSellFromSubCrate.AddRange(items);
foreach (var entry in items)
{
ItemsInSellFromSubCrate.Add(entry.Key, entry.Value);
}
OnItemsInSellFromSubCrateChanged?.Invoke();
}
public void SetSoldItems(List<SoldItem> items)
public void SetSoldItems(Dictionary<Identifier, List<SoldItem>> items)
{
if (SoldItems.Count == 0 && items.Count == 0) { return; }
SoldItems.Clear();
SoldItems.AddRange(items);
foreach (var entry in items)
{
SoldItems.Add(entry.Key, entry.Value);
}
foreach (var se in SoldEntities)
{
if (se.Status == SoldEntity.SellStatus.Confirmed) { continue; }
if (SoldItems.Any(si => Match(si, se, true)))
if (SoldItems.Any(si => si.Value.Any(si => Match(si, se, true))))
{
se.Status = SoldEntity.SellStatus.Confirmed;
}
@@ -75,13 +86,16 @@ namespace Barotrauma
se.Status = SoldEntity.SellStatus.Unconfirmed;
}
}
foreach (var si in SoldItems)
foreach (var soldItems in SoldItems.Values)
{
if (si.Origin != SoldItem.SellOrigin.Submarine) { continue; }
if (!(SoldEntities.FirstOrDefault(se => se.Item == null && Match(si, se, false)) is SoldEntity soldEntityMatch)) { continue; }
if (!(Entity.FindEntityByID(si.ID) is Item item)) { continue; }
soldEntityMatch.SetItem(item);
soldEntityMatch.Status = SoldEntity.SellStatus.Confirmed;
foreach (var si in soldItems)
{
if (si.Origin != SoldItem.SellOrigin.Submarine) { continue; }
if (!(SoldEntities.FirstOrDefault(se => se.Item == null && Match(si, se, false)) is SoldEntity soldEntityMatch)) { continue; }
if (!(Entity.FindEntityByID(si.ID) is Item item)) { continue; }
soldEntityMatch.SetItem(item);
soldEntityMatch.Status = SoldEntity.SellStatus.Confirmed;
}
}
OnSoldItemsChanged?.Invoke();
@@ -94,45 +108,24 @@ namespace Barotrauma
}
}
public void ModifyItemQuantityInSellCrate(ItemPrefab itemPrefab, int changeInQuantity)
public void ModifyItemQuantityInSellCrate(Identifier storeIdentifier, ItemPrefab itemPrefab, int changeInQuantity)
{
var itemToSell = ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab);
if (itemToSell != null)
if (GetSellCrateItem(storeIdentifier, itemPrefab) is { } item)
{
itemToSell.Quantity += changeInQuantity;
if (itemToSell.Quantity < 1)
item.Quantity += changeInQuantity;
if (item.Quantity < 1)
{
ItemsInSellCrate.Remove(itemToSell);
GetSellCrateItems(storeIdentifier)?.Remove(item);
}
}
else if (changeInQuantity > 0)
{
itemToSell = new PurchasedItem(itemPrefab, changeInQuantity);
ItemsInSellCrate.Add(itemToSell);
GetSellCrateItems(storeIdentifier, create: true).Add(new PurchasedItem(itemPrefab, changeInQuantity));
}
OnItemsInSellCrateChanged?.Invoke();
}
public void ModifyItemQuantityInSellFromSubCrate(ItemPrefab itemPrefab, int changeInQuantity)
{
var itemToSell = ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == itemPrefab);
if (itemToSell != null)
{
itemToSell.Quantity += changeInQuantity;
if (itemToSell.Quantity < 1)
{
ItemsInSellFromSubCrate.Remove(itemToSell);
}
}
else if (changeInQuantity > 0)
{
itemToSell = new PurchasedItem(itemPrefab, changeInQuantity);
ItemsInSellFromSubCrate.Add(itemToSell);
}
OnItemsInSellFromSubCrateChanged?.Invoke();
}
public void SellItems(List<PurchasedItem> itemsToSell, Store.StoreTab sellingMode)
public void SellItems(Identifier storeIdentifier, List<PurchasedItem> itemsToSell, Store.StoreTab sellingMode)
{
IEnumerable<Item> sellableItems;
try
@@ -146,19 +139,24 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error selling items: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
DebugConsole.ShowError($"Error selling items: uknown store tab type \"{sellingMode}\".\n{e.StackTrace.CleanupStackTrace()}");
return;
}
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
byte sellerId = GameMain.Client?.ID ?? 0;
// Check all the prices before starting the transaction
// to make sure the modifiers stay the same for the whole transaction
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
foreach (PurchasedItem item in itemsToSell)
// Check all the prices before starting the transaction to make sure the modifiers stay the same for the whole transaction
var sellValues = GetSellValuesAtCurrentLocation(storeIdentifier, itemsToSell.Select(i => i.ItemPrefab));
if (!(Location.GetStore(storeIdentifier) is { } store))
{
DebugConsole.ShowError($"Error selling items at {Location}: no store with identifier \"{storeIdentifier}\" exists.\n{Environment.StackTrace.CleanupStackTrace()}");
return;
}
var storeSpecificSoldItems = GetSoldItems(storeIdentifier, create: true);
foreach (var item in itemsToSell)
{
int itemValue = item.Quantity * sellValues[item.ItemPrefab];
// check if the store can afford the item
if (Location.StoreCurrentBalance < itemValue) { continue; }
if (store.Balance < itemValue) { continue; }
// TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
var matchingItems = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
int count = Math.Min(item.Quantity, matchingItems.Count());
@@ -168,9 +166,9 @@ namespace Barotrauma
for (int i = 0; i < count; i++)
{
var matchingItem = matchingItems.ElementAt(i);
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId, origin));
storeSpecificSoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId, origin));
SoldEntities.Add(new SoldEntity(matchingItem, campaign.IsSinglePlayer ? SoldEntity.SellStatus.Confirmed : SoldEntity.SellStatus.Local));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
if (canAddToRemoveQueue) { Entity.Spawner.AddItemToRemoveQueue(matchingItem); }
}
}
else
@@ -178,22 +176,26 @@ namespace Barotrauma
// When selling from the sub in multiplayer, the server will determine the items that are sold
for (int i = 0; i < count; i++)
{
SoldItems.Add(new SoldItem(item.ItemPrefab, Entity.NullEntityID, canAddToRemoveQueue, sellerId, origin));
storeSpecificSoldItems.Add(new SoldItem(item.ItemPrefab, Entity.NullEntityID, canAddToRemoveQueue, sellerId, origin));
SoldEntities.Add(new SoldEntity(item.ItemPrefab, SoldEntity.SellStatus.Local));
}
}
// Exchange money
Location.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier);
store.Balance -= itemValue;
if (GameMain.IsSingleplayer)
{
campaign.Bank.Give(itemValue);
}
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier.Value);
// Remove from the sell crate
if ((sellingMode == Store.StoreTab.Sell ? ItemsInSellCrate : ItemsInSellFromSubCrate)?.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } itemToSell)
var sellCrate = (sellingMode == Store.StoreTab.Sell ? GetSellCrateItems(storeIdentifier) : GetSubCrateItems(storeIdentifier));
if (sellCrate?.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } itemToSell)
{
itemToSell.Quantity -= item.Quantity;
if (itemToSell.Quantity < 1)
{
(sellingMode == Store.StoreTab.Sell ? ItemsInSellCrate : ItemsInSellFromSubCrate)?.Remove(itemToSell);
sellCrate.Remove(itemToSell);
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -52,39 +53,39 @@ namespace Barotrauma
text = text.TrimEnd('\n');
List<RichTextData> richTextDatas = RichTextData.GetRichTextData(text, out text) ?? new List<RichTextData>();
ImmutableArray<RichTextData>? richTextDatas = RichTextData.GetRichTextData(text, out text);
Vector2 size = GUI.SmallFont.MeasureString(text);
Vector2 size = GUIStyle.SmallFont.MeasureString(text);
Vector2 infoPos = new Vector2(GameMain.GraphicsWidth - size.X - 16, pos.Y + 8);
Rectangle infoRect = new Rectangle(infoPos.ToPoint(), size.ToPoint());
infoRect.Inflate(8, 8);
GUI.DrawRectangle(spriteBatch, infoRect, Color.Black * 0.8f, isFilled: true);
GUI.DrawRectangle(spriteBatch, infoRect, Color.White * 0.8f);
if (richTextDatas.Any())
if (richTextDatas != null && richTextDatas.Value.Any())
{
GUI.DrawStringWithColors(spriteBatch, infoPos, text, Color.White, richTextDatas, font: GUI.SmallFont);
GUI.DrawStringWithColors(spriteBatch, infoPos, text, Color.White, richTextDatas.Value, font: GUIStyle.SmallFont);
}
else
{
GUI.DrawString(spriteBatch, infoPos, text, Color.White, font: GUI.SmallFont);
GUI.DrawString(spriteBatch, infoPos, text, Color.White, font: GUIStyle.SmallFont);
}
float y = infoRect.Bottom + 16;
if (Campaign.Factions != null)
{
const string factionHeader = "Reputations";
Vector2 factionHeaderSize = GUI.SubHeadingFont.MeasureString(factionHeader);
Vector2 factionHeaderSize = GUIStyle.SubHeadingFont.MeasureString(factionHeader);
Vector2 factionPos = new Vector2(GameMain.GraphicsWidth - (264 / 2) - factionHeaderSize.X / 2, y);
GUI.DrawString(spriteBatch, factionPos, factionHeader, Color.White, font: GUI.SubHeadingFont);
GUI.DrawString(spriteBatch, factionPos, factionHeader, Color.White, font: GUIStyle.SubHeadingFont);
y += factionHeaderSize.Y + 8;
foreach (Faction faction in Campaign.Factions)
{
string name = faction.Prefab.Name;
Vector2 nameSize = GUI.SmallFont.MeasureString(name);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 264, y), name, Color.White, font: GUI.SmallFont);
LocalizedString name = faction.Prefab.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;
Color color = ToolBox.GradientLerp(faction.Reputation.NormalizedValue, Color.Red, Color.Yellow, Color.LightGreen);
@@ -98,8 +99,8 @@ namespace Barotrauma
if (location?.Reputation != null)
{
string name = Campaign.Map?.CurrentLocation.Name;
Vector2 nameSize = GUI.SmallFont.MeasureString(name);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 264, y), name, Color.White, font: GUI.SmallFont);
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);
@@ -107,8 +108,6 @@ namespace Barotrauma
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);
}
richTextDatas.Clear();
}
}
}

View File

@@ -0,0 +1,26 @@
namespace Barotrauma
{
internal partial class Wallet
{
public bool IsOwnWallet =>
GameMain.GameSession?.Campaign switch
{
null => false,
SinglePlayerCampaign spCampaign => this == spCampaign.Bank,
MultiPlayerCampaign mpCampaign => this == mpCampaign.PersonalWallet,
_ => false
};
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged)
{
CampaignMode campaign = GameMain.GameSession?.Campaign;
WalletChangedData data = new WalletChangedData
{
BalanceChanged = balanceChanged,
RewardDistributionChanged = rewardChanged,
};
campaign?.OnMoneyChanged.Invoke(new WalletChangedEvent(this, data, CreateWalletInfo()));
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Barotrauma
protected bool crewDead;
protected Color overlayColor;
protected string overlayText, overlayTextBottom;
protected LocalizedString overlayText, overlayTextBottom;
protected Color overlayTextColor;
protected Sprite overlaySprite;
@@ -63,13 +63,19 @@ namespace Barotrauma
}
}
/// <summary>
/// Gets the current personal wallet
/// In singleplayer this is the campaign bank and in multiplayer this is the personal wallet
/// </summary>
public virtual Wallet Wallet => GetWallet();
public override void ShowStartMessage()
{
foreach (Mission mission in Missions.ToList())
{
new GUIMessageBox(
mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name,
mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon, parseRichText: true)
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)
{
IconColor = mission.Prefab.IconColor,
UserData = "missionstartmessage"
@@ -80,30 +86,14 @@ namespace Barotrauma
/// <summary>
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
/// </summary>
public bool AllowedToEndRound()
{
//allow ending the round if the client has permissions, is the owner, the only client in the server
//or if no-one has management permissions
if (GameMain.Client == null) { return true; }
return
GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.ConnectedClients.Count == 1 ||
GameMain.Client.IsServerOwner ||
GameMain.Client.ConnectedClients.None(c =>
c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageRound) || c.HasPermission(ClientPermissions.ManageCampaign)));
}
/// <summary>
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
/// </summary>
public bool AllowedToManageCampaign(ClientPermissions permissions = ClientPermissions.ManageCampaign)
public bool AllowedToManageCampaign(ClientPermissions permissions)
{
//allow managing the round if the client has permissions, is the owner, the only client in the server,
//or if no-one has management permissions
if (GameMain.Client == null) { return true; }
return
GameMain.Client.HasPermission(permissions) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.ConnectedClients.Count == 1 ||
GameMain.Client.IsServerOwner ||
GameMain.Client.ConnectedClients.None(c => c.InGame && (c.IsOwner || c.HasPermission(permissions)));
@@ -123,12 +113,12 @@ namespace Barotrauma
{
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), overlayColor, isFilled: true);
}
if (!string.IsNullOrEmpty(overlayText) && overlayTextColor.A > 0)
if (!overlayText.IsNullOrEmpty() && overlayTextColor.A > 0)
{
var backgroundSprite = GUI.Style.GetComponentStyle("CommandBackground").GetDefaultSprite();
var backgroundSprite = GUIStyle.GetComponentStyle("CommandBackground").GetDefaultSprite();
Vector2 centerPos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2;
string wrappedText = ToolBox.WrapText(overlayText, GameMain.GraphicsWidth / 3, GUI.Font);
Vector2 textSize = GUI.Font.MeasureString(wrappedText);
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,
@@ -140,11 +130,11 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, textPos + Vector2.One, wrappedText, Color.Black * (overlayTextColor.A / 255.0f));
GUI.DrawString(spriteBatch, textPos, wrappedText, overlayTextColor);
if (!string.IsNullOrEmpty(overlayTextBottom))
if (!overlayTextBottom.IsNullOrEmpty())
{
Vector2 bottomTextPos = centerPos + new Vector2(0.0f, textSize.Y / 2 + 40 * GUI.Scale) - GUI.Font.MeasureString(overlayTextBottom) / 2;
GUI.DrawString(spriteBatch, bottomTextPos + Vector2.One, overlayTextBottom, Color.Black * (overlayTextColor.A / 255.0f));
GUI.DrawString(spriteBatch, bottomTextPos, overlayTextBottom, overlayTextColor);
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);
}
}
}
@@ -159,7 +149,7 @@ namespace Barotrauma
endRoundButton.Visible = false;
var availableTransition = GetAvailableTransition(out _, out Submarine leavingSub);
string buttonText = "";
LocalizedString buttonText = "";
switch (availableTransition)
{
case TransitionType.ProgressToNextLocation:
@@ -188,7 +178,7 @@ namespace Barotrauma
case TransitionType.None:
default:
if (Level.Loaded.Type == LevelData.LevelType.Outpost &&
(Character.Controlled?.Submarine?.Info.Type == SubmarineType.Player || (Character.Controlled?.CurrentHull?.OutpostModuleTags.Contains("airlock") ?? 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]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
@@ -204,7 +194,7 @@ namespace Barotrauma
if (endRoundButton.Visible)
{
if (!AllowedToEndRound())
if (!AllowedToManageCampaign(ClientPermissions.ManageMap))
{
buttonText = TextManager.Get("map");
}
@@ -218,7 +208,7 @@ namespace Barotrauma
endRoundButton.OnClicked(EndRoundButton, null);
prevCampaignUIAutoOpenType = availableTransition;
}
endRoundButton.Text = ToolBox.LimitString(buttonText, endRoundButton.Font, endRoundButton.Rect.Width - 5);
endRoundButton.Text = ToolBox.LimitString(buttonText.Value, endRoundButton.Font, endRoundButton.Rect.Width - 5);
if (endRoundButton.Text != buttonText)
{
endRoundButton.ToolTip = buttonText;
@@ -244,7 +234,7 @@ namespace Barotrauma
if (ReadyCheck.ReadyCheckCooldown > DateTime.Now)
{
float progress = (ReadyCheck.ReadyCheckCooldown - DateTime.Now).Seconds / 60.0f;
ReadyCheckButton.Color = ToolBox.GradientLerp(progress, Color.White, GUI.Style.Red);
ReadyCheckButton.Color = ToolBox.GradientLerp(progress, Color.White, GUIStyle.Red);
}
}
}
@@ -289,7 +279,7 @@ namespace Barotrauma
case InteractionType.Examine:
return;
case InteractionType.Upgrade when !UpgradeManager.CanUpgradeSub():
UpgradeManager.CreateUpgradeErrorMessage(TextManager.Get("Dialog.CantUpgrade"), IsSinglePlayer, npc);
UpgradeManager.CreateUpgradeErrorMessage(TextManager.Get("Dialog.CantUpgrade").Value, IsSinglePlayer, npc);
return;
case InteractionType.Crew when GameMain.NetworkMember != null:
CampaignUI.CrewManagement.SendCrewState(false);
@@ -299,8 +289,8 @@ namespace Barotrauma
goto default;
default:
ShowCampaignUI = true;
CampaignUI.SelectTab(npc.CampaignInteractionType);
CampaignUI.UpgradeStore?.RefreshAll();
CampaignUI.SelectTab(npc.CampaignInteractionType, storeIdentifier: npc.MerchantIdentifier);
CampaignUI.UpgradeStore?.RequestRefresh();
break;
}
}
@@ -310,7 +300,7 @@ namespace Barotrauma
if (ShowCampaignUI || ForceMapUI)
{
campaignUIContainer?.AddToGUIUpdateList();
if (CampaignUI?.UpgradeStore?.HoveredItem != null)
if (CampaignUI?.UpgradeStore?.HoveredEntity != null)
{
if (CampaignUI.SelectedTab != InteractionType.Upgrade) { return; }
CampaignUI?.UpgradeStore?.ItemInfoFrame.AddToGUIUpdateList(order: 1);

View File

@@ -21,7 +21,7 @@ namespace Barotrauma
private UInt16 pendingSaveID = 1;
public UInt16 PendingSaveID
{
get
get
{
return pendingSaveID;
}
@@ -34,7 +34,15 @@ namespace Barotrauma
}
}
public static void StartCampaignSetup(IEnumerable<string> saveFiles)
public Wallet PersonalWallet => Character.Controlled?.Wallet ?? Wallet.Invalid;
public override Wallet Wallet => GetWallet();
public override Wallet GetWallet(Client client = null)
{
return PersonalWallet;
}
public static void StartCampaignSetup(List<SaveInfo> saveFiles)
{
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
parent.ClearChildren();
@@ -195,6 +203,11 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
}
if (GameMain.Client == null)
{
yield return CoroutineStatus.Failure;
}
if (GameMain.Client.LateCampaignJoin)
{
GameMain.Client.LateCampaignJoin = false;
@@ -217,8 +230,7 @@ namespace Barotrauma
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
overlayTextColor = Color.Transparent;
overlayText = TextManager.GetWithVariables("campaignstart",
new string[] { "xxxx", "yyyy" },
new string[] { Map.CurrentLocation.Name, TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass) });
("xxxx", Map.CurrentLocation.Name), ("yyyy", TextManager.Get($"submarineclass.{Submarine.MainSub.Info.SubmarineClass}")));
float fadeInDuration = 1.0f;
float textDuration = 10.0f;
float timer = 0.0f;
@@ -317,7 +329,7 @@ namespace Barotrauma
private IEnumerable<CoroutineStatus> DoLevelTransition()
{
SoundPlayer.OverrideMusicType = CrewManager.GetCharacters().Any(c => !c.IsDead) ? "endround" : "crewdead";
SoundPlayer.OverrideMusicType = (CrewManager.GetCharacters().Any(c => !c.IsDead) ? "endround" : "crewdead").ToIdentifier();
SoundPlayer.OverrideMusicDuration = 18.0f;
Level prevLevel = Level.Loaded;
@@ -541,36 +553,10 @@ namespace Barotrauma
msg.Write(PurchasedItemRepairs);
msg.Write(PurchasedLostShuttles);
msg.Write((UInt16)CargoManager.ItemsInBuyCrate.Count);
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.ItemsInSellFromSubCrate.Count);
foreach (PurchasedItem pi in CargoManager.ItemsInSellFromSubCrate)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.SoldItems.Count);
foreach (SoldItem si in CargoManager.SoldItems)
{
msg.Write(si.ItemPrefab.Identifier);
msg.Write((UInt16)si.ID);
msg.Write(si.Removed);
msg.Write(si.SellerID);
msg.Write((byte)si.Origin);
}
WriteItems(msg, CargoManager.ItemsInBuyCrate);
WriteItems(msg, CargoManager.ItemsInSellFromSubCrate);
WriteItems(msg, CargoManager.PurchasedItems);
WriteItems(msg, CargoManager.SoldItems);
msg.Write((ushort)UpgradeManager.PurchasedUpgrades.Count);
foreach (var (prefab, category, level) in UpgradeManager.PurchasedUpgrades)
@@ -584,7 +570,7 @@ namespace Barotrauma
foreach (var itemSwap in UpgradeManager.PurchasedItemSwaps)
{
msg.Write(itemSwap.ItemToRemove.ID);
msg.Write(itemSwap.ItemToInstall?.Identifier ?? string.Empty);
msg.Write(itemSwap.ItemToInstall?.Identifier ?? Identifier.Empty);
}
}
@@ -606,85 +592,63 @@ namespace Barotrauma
selectedMissionIndices.Add(msg.ReadByte());
}
ushort ownedSubCount = msg.ReadUInt16();
List<ushort> ownedSubIndices = new List<ushort>();
for (int i = 0; i < ownedSubCount; i++)
{
ownedSubIndices.Add(msg.ReadUInt16());
}
bool allowDebugTeleport = msg.ReadBoolean();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
Dictionary<string, float> factionReps = new Dictionary<string, float>();
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
byte factionsCount = msg.ReadByte();
for (int i = 0; i < factionsCount; i++)
{
factionReps.Add(msg.ReadString(), msg.ReadSingle());
factionReps.Add(msg.ReadIdentifier(), msg.ReadSingle());
}
bool forceMapUI = msg.ReadBoolean();
int money = msg.ReadInt32();
bool purchasedHullRepairs = msg.ReadBoolean();
bool purchasedItemRepairs = msg.ReadBoolean();
bool purchasedLostShuttles = msg.ReadBoolean();
byte missionCount = msg.ReadByte();
List<Pair<string, byte>> availableMissions = new List<Pair<string, byte>>();
var availableMissions = new List<(Identifier Identifier, byte ConnectionIndex)>();
for (int i = 0; i < missionCount; i++)
{
string missionIdentifier = msg.ReadString();
Identifier missionIdentifier = msg.ReadIdentifier();
byte connectionIndex = msg.ReadByte();
availableMissions.Add(new Pair<string, byte>(missionIdentifier, connectionIndex));
availableMissions.Add((missionIdentifier, connectionIndex));
}
UInt16? storeBalance = null;
var storeBalances = new Dictionary<Identifier, UInt16>();
if (msg.ReadBoolean())
{
storeBalance = msg.ReadUInt16();
byte storeCount = msg.ReadByte();
for (int i = 0; i < storeCount; i++)
{
Identifier identifier = msg.ReadIdentifier();
UInt16 storeBalance = msg.ReadUInt16();
storeBalances.Add(identifier, storeBalance);
}
}
UInt16 buyCrateItemCount = msg.ReadUInt16();
List<PurchasedItem> buyCrateItems = new List<PurchasedItem>();
for (int i = 0; i < buyCrateItemCount; i++)
{
string itemPrefabIdentifier = msg.ReadString();
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 subSellCrateItemCount = msg.ReadUInt16();
List<PurchasedItem> subSellCrateItems = new List<PurchasedItem>();
for (int i = 0; i < subSellCrateItemCount; i++)
{
string itemPrefabIdentifier = msg.ReadString();
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
subSellCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 purchasedItemCount = msg.ReadUInt16();
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
for (int i = 0; i < purchasedItemCount; i++)
{
string itemPrefabIdentifier = msg.ReadString();
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 soldItemCount = msg.ReadUInt16();
List<SoldItem> soldItems = new List<SoldItem>();
for (int i = 0; i < soldItemCount; i++)
{
string itemPrefabIdentifier = msg.ReadString();
UInt16 id = msg.ReadUInt16();
bool removed = msg.ReadBoolean();
byte sellerId = msg.ReadByte();
byte origin = msg.ReadByte();
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId, (SoldItem.SellOrigin)origin));
}
var buyCrateItems = ReadPurchasedItems(msg, sender: null);
var subSellCrateItems = ReadPurchasedItems(msg, sender: null);
var purchasedItems = ReadPurchasedItems(msg, sender: null);
var soldItems = ReadSoldItems(msg);
ushort pendingUpgradeCount = msg.ReadUInt16();
List<PurchasedUpgrade> pendingUpgrades = new List<PurchasedUpgrade>();
for (int i = 0; i < pendingUpgradeCount; i++)
{
string upgradeIdentifier = msg.ReadString();
Identifier upgradeIdentifier = msg.ReadIdentifier();
UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
string categoryIdentifier = msg.ReadString();
Identifier categoryIdentifier = msg.ReadIdentifier();
UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
int upgradeLevel = msg.ReadByte();
if (prefab == null || category == null) { continue; }
@@ -696,8 +660,8 @@ namespace Barotrauma
for (int i = 0; i < purchasedItemSwapCount; i++)
{
UInt16 itemToRemoveID = msg.ReadUInt16();
string itemToInstallIdentifier = msg.ReadString();
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
Identifier itemToInstallIdentifier = msg.ReadIdentifier();
ItemPrefab itemToInstall = itemToInstallIdentifier.IsEmpty ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
@@ -740,12 +704,29 @@ namespace Barotrauma
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndices);
GameMain.GameSession.OwnedSubmarines.Clear();
foreach (int ownedSubIndex in ownedSubIndices)
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[ownedSubIndex];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned))
{
GameMain.GameSession.OwnedSubmarines.Add(sub);
}
}
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.CargoManager.SetSoldItems(soldItems);
if (storeBalance.HasValue) { campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value; }
foreach (var balance in storeBalances)
{
if (campaign.Map.CurrentLocation.GetStore(balance.Key) is { } store)
{
store.Balance = balance.Value;
}
}
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var purchasedItemSwap in purchasedItemSwaps)
@@ -769,7 +750,7 @@ namespace Barotrauma
foreach (var (identifier, rep) in factionReps)
{
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
if (faction?.Reputation != null)
{
faction.Reputation.SetReputation(rep);
@@ -783,29 +764,29 @@ namespace Barotrauma
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
foreach (var availableMission in availableMissions)
{
MissionPrefab missionPrefab = MissionPrefab.List.Find(mp => mp.Identifier == availableMission.First);
MissionPrefab missionPrefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == availableMission.Identifier);
if (missionPrefab == null)
{
DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.First}\" not found.");
DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.Identifier}\" not found.");
continue;
}
if (availableMission.Second == 255)
if (availableMission.ConnectionIndex == 255)
{
campaign.Map.CurrentLocation.UnlockMission(missionPrefab);
}
else
{
if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count)
if (availableMission.ConnectionIndex < 0 || availableMission.ConnectionIndex >= campaign.Map.CurrentLocation.Connections.Count)
{
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.Identifier}\" out of range (index: {availableMission.ConnectionIndex}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
continue;
}
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second];
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.ConnectionIndex];
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
}
}
@@ -813,19 +794,17 @@ namespace Barotrauma
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
bool shouldRefresh = campaign.Money != money ||
campaign.PurchasedHullRepairs != purchasedHullRepairs ||
bool shouldRefresh = campaign.PurchasedHullRepairs != purchasedHullRepairs ||
campaign.PurchasedItemRepairs != purchasedItemRepairs ||
campaign.PurchasedLostShuttles != purchasedLostShuttles;
campaign.Money = money;
campaign.PurchasedHullRepairs = purchasedHullRepairs;
campaign.PurchasedItemRepairs = purchasedItemRepairs;
campaign.PurchasedLostShuttles = purchasedLostShuttles;
if (shouldRefresh)
{
campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
if (myCharacterInfo != null)
@@ -849,7 +828,7 @@ namespace Barotrauma
List<CharacterInfo> availableHires = new List<CharacterInfo>();
for (int i = 0; i < availableHireLength; i++)
{
CharacterInfo hire = CharacterInfo.ClientRead("human", msg);
CharacterInfo hire = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
hire.Salary = msg.ReadInt32();
availableHires.Add(hire);
}
@@ -865,7 +844,7 @@ namespace Barotrauma
List<CharacterInfo> hiredCharacters = new List<CharacterInfo>();
for (int i = 0; i < hiredLength; i++)
{
CharacterInfo hired = CharacterInfo.ClientRead("human", msg);
CharacterInfo hired = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
hired.Salary = msg.ReadInt32();
hiredCharacters.Add(hired);
}
@@ -897,6 +876,43 @@ namespace Barotrauma
}
}
public void ClientReadMoney(IReadMessage inc)
{
NetWalletUpdate update = INetSerializableStruct.Read<NetWalletUpdate>(inc);
foreach (NetWalletTransaction transaction in update.Transactions)
{
WalletInfo info = transaction.Info;
switch (transaction.CharacterID)
{
case Some<ushort> { Value: var charID }:
{
Character targetCharacter = Character.CharacterList?.FirstOrDefault(c => c.ID == charID);
if (targetCharacter is null) { break; }
Wallet wallet = targetCharacter.Wallet;
wallet.Balance = info.Balance;
wallet.RewardDistribution = info.RewardDistribution;
TryInvokeEvent(wallet, transaction.ChangedData, info);
break;
}
case None<ushort> _:
{
Bank.Balance = info.Balance;
TryInvokeEvent(Bank, transaction.ChangedData, info);
break;
}
}
}
void TryInvokeEvent(Wallet wallet, WalletChangedData data, WalletInfo info)
{
if (data.BalanceChanged.IsSome() || data.RewardDistributionChanged.IsSome())
{
OnMoneyChanged.Invoke(new WalletChangedEvent(wallet, data, info));
}
}
}
public override void Save(XElement element)
{
//do nothing, the clients get the save files from the server
@@ -909,10 +925,10 @@ namespace Barotrauma
string gamesessionDocPath = Path.Combine(SaveUtil.TempPath, "gamesession.xml");
XDocument doc = XMLExtensions.TryLoadXml(gamesessionDocPath);
if (doc == null)
if (doc == null)
{
DebugConsole.ThrowError($"Failed to load the state of a multiplayer campaign. Could not open the file \"{gamesessionDocPath}\".");
return;
return;
}
Load(doc.Root.Element("MultiPlayerCampaign"));
GameMain.GameSession.OwnedSubmarines = SaveUtil.LoadOwnedSubmarines(doc, out SubmarineInfo selectedSub);

View File

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -68,7 +69,7 @@ namespace Barotrauma
for (int i = 0; i < jobPrefab.InitialCount; i++)
{
var variant = Rand.Range(0, jobPrefab.Variants);
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant));
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant));
}
}
InitCampaignData();
@@ -82,7 +83,7 @@ namespace Barotrauma
{
IsFirstRound = false;
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -108,6 +109,9 @@ namespace Barotrauma
case "pets":
petsElement = subElement;
break;
case Wallet.LowerCaseSaveElementName:
Bank = new Wallet(Option<Character>.None(), subElement);
break;
case "stats":
LoadStats(subElement);
break;
@@ -121,7 +125,16 @@ namespace Barotrauma
InitUI();
Money = element.GetAttributeInt("money", 0);
//backwards compatibility for saves made prior to the addition of personal wallets
int oldMoney = element.GetAttributeInt("money", 0);
if (oldMoney > 0)
{
Bank = new Wallet(Option<Character>.None())
{
Balance = oldMoney
};
}
PurchasedLostShuttles = element.GetAttributeBool("purchasedlostshuttles", false);
PurchasedHullRepairs = element.GetAttributeBool("purchasedhullrepairs", false);
PurchasedItemRepairs = element.GetAttributeBool("purchaseditemrepairs", false);
@@ -283,9 +296,9 @@ namespace Barotrauma
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
overlayTextColor = Color.Transparent;
overlayText = TextManager.GetWithVariables(showCampaignResetText ? "campaignend4" : "campaignstart",
new string[] { "xxxx", "yyyy" },
new string[] { Map.CurrentLocation.Name, TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass) });
string pressAnyKeyText = TextManager.Get("pressanykey");
("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;
@@ -385,7 +398,7 @@ namespace Barotrauma
{
NextLevel = newLevel;
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
SoundPlayer.OverrideMusicType = success ? "endround" : "crewdead";
SoundPlayer.OverrideMusicType = (success ? "endround" : "crewdead").ToIdentifier();
SoundPlayer.OverrideMusicDuration = 18.0f;
GUI.SetSavingIndicatorState(success);
crewDead = false;
@@ -672,9 +685,9 @@ namespace Barotrauma
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
if (subsToLeaveBehind.Any())
{
string msg = TextManager.Get(subsToLeaveBehind.Count == 1 ? "LeaveSubBehind" : "LeaveSubsBehind");
LocalizedString msg = TextManager.Get(subsToLeaveBehind.Count == 1 ? "LeaveSubBehind" : "LeaveSubsBehind");
var msgBox = new GUIMessageBox(TextManager.Get("Warning"), msg, new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
var msgBox = new GUIMessageBox(TextManager.Get("Warning"), msg, new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked += (btn, userdata) => { LoadNewLevel(); return true; } ;
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[0].UserData = Submarine.Loaded.FindAll(s => !subsToLeaveBehind.Contains(s));
@@ -729,7 +742,6 @@ namespace Barotrauma
public override void Save(XElement element)
{
XElement modeElement = new XElement("SinglePlayerCampaign",
new XAttribute("money", Money),
new XAttribute("purchasedlostshuttles", PurchasedLostShuttles),
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
@@ -756,13 +768,14 @@ namespace Barotrauma
petsElement = new XElement("pets");
PetBehavior.SavePets(petsElement);
modeElement.Add(petsElement);
modeElement.Add(petsElement);
CrewManager.Save(modeElement);
CampaignMetadata.Save(modeElement);
Map.Save(modeElement);
CargoManager?.SavePurchasedItems(modeElement);
UpgradeManager?.Save(modeElement);
modeElement.Add(Bank.Save());
element.Add(modeElement);
}
}

View File

@@ -24,12 +24,12 @@ namespace Barotrauma
public TestGameMode(GameModePreset preset) : base(preset)
{
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs.OrderBy(p => p.Identifier))
{
for (int i = 0; i < jobPrefab.InitialCount; i++)
{
var variant = Rand.Range(0, jobPrefab.Variants);
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant));
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant));
}
}
}
@@ -93,7 +93,7 @@ namespace Barotrauma
private void GenerateOutpost(Submarine submarine)
{
Submarine outpost = OutpostGenerator.Generate(OutpostParams ?? OutpostGenerationParams.Params.GetRandom(), OutpostType ?? LocationType.List.GetRandom());
Submarine outpost = OutpostGenerator.Generate(OutpostParams ?? OutpostGenerationParams.OutpostParams.GetRandomUnsynced(), OutpostType ?? LocationType.Prefabs.GetRandomUnsynced());
outpost.SetPosition(Vector2.Zero);
float closestDistance = 0.0f;
@@ -131,7 +131,7 @@ namespace Barotrauma
if (Character.Controlled != null)
{
Character.Controlled.TeleportTo(outpost.GetWaypoints(false).GetRandom(point => point.SpawnType == SpawnType.Human).WorldPosition);
Character.Controlled.TeleportTo(outpost.GetWaypoints(false).GetRandomUnsynced(point => point.SpawnType == SpawnType.Human).WorldPosition);
}
}
}

View File

@@ -1,683 +0,0 @@
using Barotrauma.Items.Components;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
class BasicTutorial : ScenarioTutorial
{
public BasicTutorial(XElement element)
: base(element)
{
}
public override IEnumerable<CoroutineStatus> UpdateState()
{
Character Controlled = Character.Controlled;
if (Controlled == null) yield return CoroutineStatus.Success;
foreach (Item item in Item.ItemList)
{
var wire = item.GetComponent<Wire>();
if (wire != null && wire.Connections.Any(c => c != null))
{
wire.Locked = true;
}
}
//remove all characters except the controlled one to prevent any unintended monster attacks
var existingCharacters = Character.CharacterList.FindAll(c => c != Controlled);
foreach (Character c in existingCharacters)
{
c.Remove();
}
yield return new WaitForSeconds(4.0f);
infoBox = CreateInfoFrame("", "Use WASD to move and the mouse to look around");
yield return new WaitForSeconds(5.0f);
//-----------------------------------
infoBox = CreateInfoFrame("", "Open the door at your right side by highlighting the button next to it with your cursor and pressing E");
Door tutorialDoor = Item.ItemList.Find(i => i.HasTag("tutorialdoor")).GetComponent<Door>();
while (!tutorialDoor.IsOpen && Controlled.WorldPosition.X < tutorialDoor.Item.WorldPosition.X)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(2.0f);
//-----------------------------------
infoBox = CreateInfoFrame("", "Hold W or S to walk up or down stairs. Use shift to run.", hasButton: true);
while (infoBox != null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
//-----------------------------------
infoBox = CreateInfoFrame("", "At the moment the submarine has no power, which means that crucial systems such as the oxygen generator or the engine aren't running. Let's fix this: go to the upper left corner of the submarine, where you'll find a nuclear reactor.");
Reactor reactor = Item.ItemList.Find(i => i.HasTag("tutorialreactor")).GetComponent<Reactor>();
//reactor.MeltDownTemp = 20000.0f;
while (Vector2.Distance(Controlled.Position, reactor.Item.Position) > 200.0f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "The reactor requires fuel rods to generate power. You can grab one from the steel cabinet by walking next to it and pressing E.");
while (Controlled.SelectedConstruction == null || Controlled.SelectedConstruction.Prefab.Identifier != "steelcabinet")
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Pick up one of the fuel rods either by double-clicking or dragging and dropping it into your inventory.");
while (!HasItem("fuelrod"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Select the reactor by walking next to it and pressing E.");
while (Controlled.SelectedConstruction != reactor.Item)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("", "Load the fuel rod into the reactor by dropping it into any of the 5 slots.");
while (reactor.AvailableFuel <= 0.0f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "The reactor is now fueled up. Try turning it on by increasing the fission rate.");
while (reactor.FissionRate <= 0.0f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("", "The reactor core has started generating heat, which in turn generates power for the submarine. The power generation is very low at the moment,"
+ " because the reactor is set to shut itself down when the temperature rises above 500 degrees Celsius. You can adjust the temperature limit by changing the \"Shutdown Temperature\" in the control panel.", hasButton: true);
//TODO: reimplement
/*while (infoBox != null)
{
reactor.ShutDownTemp = Math.Min(reactor.ShutDownTemp, 5000.0f);
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("The amount of power generated by the reactor should be kept close to the amount of power consumed by the devices in the submarine. "
+ "If there isn't enough power, devices won't function properly (or at all), and if there's too much power, some devices may be damaged."
+ " Try to raise the temperature of the reactor close to 3000 degrees by adjusting the fission and cooling rates.", true);
while (Math.Abs(reactor.Temperature - 3000.0f) > 100.0f)
{
reactor.AutoTemp = false;
reactor.ShutDownTemp = Math.Min(reactor.ShutDownTemp, 5000.0f);
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("Looks like we're up and running! Now you should turn on the \"Automatic temperature control\", which will make the reactor "
+ "automatically adjust the temperature to a suitable level. Even though it's an easy way to keep the reactor up and running most of the time, "
+ "you should keep in mind that it changes the temperature very slowly and carefully, which may cause issues if there are sudden changes in grid load.");
while (!reactor.AutoTemp)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}*/
yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("", "That's the basics of operating the reactor! Now that there's power available for the engines, it's time to get the submarine moving. "
+ "Deselect the reactor by pressing E and head to the command room at the right edge of the vessel.");
Steering steering = Item.ItemList.Find(i => i.HasTag("tutorialsteering")).GetComponent<Steering>();
Sonar sonar = steering.Item.GetComponent<Sonar>();
while (Vector2.Distance(Controlled.Position, steering.Item.Position) > 150.0f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
CoroutineManager.StartCoroutine(KeepReactorRunning(reactor));
infoBox = CreateInfoFrame("", "Select the navigation terminal by walking next to it and pressing E.");
while (Controlled.SelectedConstruction != steering.Item)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("", "There seems to be something wrong with the navigation terminal." +
" There's nothing on the monitor, so it's probably out of power. The reactor must still be"
+ " running or the lights would've gone out, so it's most likely a problem with the wiring."
+ " Deselect the terminal by pressing E to start checking the wiring.");
while (Controlled.SelectedConstruction == steering.Item)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(1.0f);
infoBox = CreateInfoFrame("", "You need a screwdriver to check the wiring of the terminal."
+ " Equip a screwdriver by pulling it to either of the slots with a hand symbol, and then use it on the terminal by left clicking.");
while (Controlled.SelectedConstruction != steering.Item ||
Controlled.HeldItems.FirstOrDefault(i => i.Prefab.Identifier == "screwdriver") == null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Here you can see all the wires connected to the terminal. Apparently there's no wire"
+ " going into the to the power connection - that's why the monitor isn't working."
+ " You should find a piece of wire to connect it. Try searching some of the cabinets scattered around the sub.");
while (!HasItem("wire"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Head back to the navigation terminal to fix the wiring.");
PowerTransfer junctionBox = Item.ItemList.Find(i => i != null && i.HasTag("tutorialjunctionbox")).GetComponent<PowerTransfer>();
while ((Controlled.SelectedConstruction != junctionBox.Item &&
Controlled.SelectedConstruction != steering.Item) ||
!Controlled.HeldItems.Any(i => i.Prefab.Identifier == "screwdriver"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
if (!Controlled.HeldItems.Any(i => i.GetComponent<Wire>() != null))
{
infoBox = CreateInfoFrame("", "Equip the wire by dragging it to one of the slots with a hand symbol.");
while (!Controlled.HeldItems.Any(i => i.GetComponent<Wire>() != null))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
}
infoBox = CreateInfoFrame("", "You can see the equipped wire at the middle of the connection panel. Drag it to the power connector.");
var steeringConnection = steering.Item.Connections.Find(c => c.Name.Contains("power"));
while (steeringConnection.Wires.FirstOrDefault(w => w != null) == null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Now you have to connect the other end of the wire to a power source. "
+ "The junction box in the room just below the command room should do.");
while (Controlled.SelectedConstruction != null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(2.0f);
infoBox = CreateInfoFrame("", "You can now move the other end of the wire around, and attach it on the wall by left clicking or "
+ "remove the previous attachment by right clicking. Or if you don't care for neatly laid out wiring, you can just "
+ "run it straight to the junction box.");
while (Controlled.SelectedConstruction == null || Controlled.SelectedConstruction.GetComponent<PowerTransfer>() == null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Connect the wire to the junction box by pulling it to the power connection, the same way you did with the navigation terminal.");
while (sonar.Voltage < 0.1f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Great! Now we should be able to get moving.");
while (Controlled.SelectedConstruction != steering.Item)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "You can take a look at the area around the sub by selecting the \"Active Sonar\" checkbox.");
while (!sonar.IsActive)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("", "The blue rectangle in the middle is the submarine, and the flickering shapes outside it are the walls of an underwater cavern. "
+ "Try moving the submarine by clicking somewhere on the monitor and dragging the pointer to the direction you want to go to.");
while (steering.TargetVelocity == Vector2.Zero && steering.TargetVelocity.Length() < 50.0f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(4.0f);
infoBox = CreateInfoFrame("", "The submarine moves up and down by pumping water in and out of the two ballast tanks at the bottom of the submarine. "
+ "The engine at the back of the sub moves it forwards and backwards.", hasButton: true);
while (infoBox != null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Steer the submarine downwards, heading further into the cavern.");
while (Submarine.MainSub.WorldPosition.Y > 32000.0f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(1.0f);
var moloch = Character.Create("moloch", steering.Item.WorldPosition + new Vector2(3000.0f, -500.0f), "");
moloch.PlaySound(CharacterSound.SoundType.Attack);
yield return new WaitForSeconds(1.0f);
infoBox = CreateInfoFrame("", "Uh-oh... Something enormous just appeared on the sonar.");
List<Structure> windows = new List<Structure>();
foreach (Structure s in Structure.WallList)
{
if (s.CastShadow || !s.HasBody) continue;
if (s.Rect.Right > steering.Item.CurrentHull.Rect.Right) windows.Add(s);
}
float slowdownTimer = 1.0f;
bool broken = false;
do
{
steering.TargetVelocity = Vector2.Zero;
slowdownTimer = Math.Max(0.0f, slowdownTimer - CoroutineManager.DeltaTime * 0.3f);
Submarine.MainSub.Velocity *= slowdownTimer;
moloch.AIController.SelectTarget(steering.Item.CurrentHull.AiTarget);
Vector2 steeringDir = windows[0].WorldPosition - moloch.WorldPosition;
if (steeringDir != Vector2.Zero) steeringDir = Vector2.Normalize(steeringDir);
moloch.AIController.SteeringManager.SteeringManual(CoroutineManager.DeltaTime, steeringDir * 100.0f);
foreach (Structure window in windows)
{
for (int i = 0; i < window.SectionCount; i++)
{
if (!window.SectionIsLeaking(i)) continue;
broken = true;
break;
}
if (broken) break;
}
if (broken) break;
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
} while (!broken);
//fix everything except the command windows
foreach (Structure w in Structure.WallList)
{
bool isWindow = windows.Contains(w);
for (int i = 0; i < w.SectionCount; i++)
{
if (!w.SectionIsLeaking(i)) continue;
if (isWindow)
{
//decrease window damage to slow down the leaking
w.AddDamage(i, -w.SectionDamage(i) * 0.48f);
}
else
{
w.AddDamage(i, -100000.0f);
}
}
}
Submarine.MainSub.GodMode = true;
var capacitor1 = Item.ItemList.Find(i => i.HasTag("capacitor1")).GetComponent<PowerContainer>();
var capacitor2 = Item.ItemList.Find(i => i.HasTag("capacitor1")).GetComponent<PowerContainer>();
CoroutineManager.StartCoroutine(KeepEnemyAway(moloch, new PowerContainer[] { capacitor1, capacitor2 }));
infoBox = CreateInfoFrame("", "The hull has been breached! Close all the doors to the command room to stop the water from flooding the entire sub!");
Door commandDoor1 = Item.ItemList.Find(i => i.HasTag("commanddoor1")).GetComponent<Door>();
Door commandDoor2 = Item.ItemList.Find(i => i.HasTag("commanddoor2")).GetComponent<Door>();
//wait until the player is out of the room and the doors are closed
while (Controlled.WorldPosition.X > commandDoor1.Item.WorldPosition.X ||
(commandDoor1.IsOpen || commandDoor2.IsOpen))
{
//prevent the hull from filling up completely and crushing the player
steering.Item.CurrentHull.WaterVolume = Math.Min(steering.Item.CurrentHull.WaterVolume, steering.Item.CurrentHull.Volume * 0.9f);
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "You should quickly find yourself a diving mask or a diving suit. " +
"There are some in the room next to the airlock.");
bool divingMaskSelected = false;
while (!HasItem("divingmask") && !HasItem("divingsuit"))
{
if (!divingMaskSelected &&
Controlled.FocusedItem != null && Controlled.FocusedItem.Prefab.Identifier == "divingsuit")
{
infoBox = CreateInfoFrame("", "There can only be one item in each inventory slot, so you need to take off "
+ "the jumpsuit if you wish to wear a diving suit.");
divingMaskSelected = true;
}
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
if (HasItem("divingmask"))
{
infoBox = CreateInfoFrame("", "The diving mask will let you breathe underwater, but it won't protect from the water pressure outside the sub. " +
"It should be fine for the situation at hand, but you still need to find an oxygen tank and drag it into the same slot as the mask." +
"You should grab one or two from one of the cabinets.");
}
else if (HasItem("divingsuit"))
{
infoBox = CreateInfoFrame("", "In addition to letting you breathe underwater, the suit will protect you from the water pressure outside the sub " +
"(unlike the diving mask). However, you still need to drag an oxygen tank into the same slot as the suit to supply oxygen. " +
"You should grab one or two from one of the cabinets.");
}
while (!HasItem("oxygentank"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
yield return new WaitForSeconds(5.0f);
infoBox = CreateInfoFrame("", "Now you should stop the creature attacking the submarine before it does any more damage. Head to the railgun room at the upper right corner of the sub.");
var railGun = Item.ItemList.Find(i => i.GetComponent<Turret>() != null);
while (Vector2.Distance(Controlled.Position, railGun.Position) > 500)
{
yield return new WaitForSeconds(1.0f);
}
infoBox = CreateInfoFrame("", "The railgun requires a large power surge to fire. The reactor can't provide a surge large enough, so we need to use the "
+ " supercapacitors in the railgun room. The capacitors need to be charged first; select them and crank up the recharge rate.");
while (capacitor1.RechargeSpeed < 0.5f && capacitor2.RechargeSpeed < 0.5f)
{
yield return new WaitForSeconds(1.0f);
}
infoBox = CreateInfoFrame("", "The capacitors take some time to recharge, so now is a good " +
"time to head to the room below and load some shells for the railgun.");
var loader = Item.ItemList.Find(i => i.Prefab.Identifier == "railgunloader").GetComponent<ItemContainer>();
while (Math.Abs(Controlled.Position.Y - loader.Item.Position.Y) > 80)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Grab one of the shells. You can load it by selecting the railgun loader and dragging the shell to. "
+ "one of the free slots. You need two hands to carry a shell, so make sure you don't have anything else in either hand.");
while (loader.Item.ContainedItems.FirstOrDefault(i => i != null && i.Prefab.Identifier == "railgunshell") == null)
{
//TODO: reimplement
//moloch.Health = 50.0f;
capacitor1.Charge += 5.0f;
capacitor2.Charge += 5.0f;
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "Now we're ready to shoot! Select the railgun controller.");
while (Controlled.SelectedConstruction == null || Controlled.SelectedConstruction.Prefab.Identifier != "railguncontroller")
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
moloch.AnimController.SetPosition(ConvertUnits.ToSimUnits(Controlled.WorldPosition + Vector2.UnitY * 600.0f));
infoBox = CreateInfoFrame("", "Use the right mouse button to aim and wait for the creature to come closer. When you're ready to shoot, "
+ "press the left mouse button.");
while (!moloch.IsDead)
{
if (moloch.WorldPosition.Y > Controlled.WorldPosition.Y + 600.0f)
{
moloch.AIController.SteeringManager.SteeringManual(CoroutineManager.DeltaTime, Controlled.WorldPosition - moloch.WorldPosition);
}
moloch.AIController.SelectTarget(Controlled.AiTarget);
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
Submarine.MainSub.GodMode = false;
infoBox = CreateInfoFrame("", "The creature has died. Now you should fix the damages in the control room: " +
"Grab a welding tool from the closet in the railgun room.");
while (!HasItem("weldingtool"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "The welding tool requires fuel to work. Grab a welding fuel tank and attach it to the tool " +
"by dragging it into the same slot.");
do
{
var weldingTool = Controlled.Inventory.FindItemByIdentifier("weldingtool");
if (weldingTool != null &&
weldingTool.ContainedItems.FirstOrDefault(contained => contained != null && contained.Prefab.Identifier == "weldingfueltank") != null) break;
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
} while (true);
infoBox = CreateInfoFrame("", "You can aim with the tool using the right mouse button and weld using the left button. " +
"Head to the command room to fix the leaks there.");
do
{
broken = false;
foreach (Structure window in windows)
{
for (int i = 0; i < window.SectionCount; i++)
{
if (!window.SectionIsLeaking(i)) continue;
broken = true;
break;
}
if (broken) break;
}
yield return new WaitForSeconds(1.0f);
} while (broken);
infoBox = CreateInfoFrame("", "The hull is fixed now, but there's still quite a bit of water inside the sub. It should be pumped out "
+ "using the bilge pump in the room at the bottom of the submarine.");
Pump pump = Item.ItemList.Find(i => i.HasTag("tutorialpump")).GetComponent<Pump>();
while (Vector2.Distance(Controlled.Position, pump.Item.Position) > 100.0f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "The two pumps inside the ballast tanks "
+ "are connected straight to the navigation terminal and can't be manually controlled unless you mess with their wiring, " +
"so you should only use the pump in the middle room to pump out the water. Select it, turn it on and adjust the pumping speed " +
"to start pumping water out.", hasButton: true);
while (infoBox != null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
bool brokenMsgShown = false;
Item brokenBox = null;
while (pump.FlowPercentage > 0.0f || pump.CurrFlow <= 0.0f || !pump.IsActive)
{
if (!brokenMsgShown && pump.Voltage < pump.MinVoltage && Controlled.SelectedConstruction == pump.Item)
{
brokenMsgShown = true;
infoBox = CreateInfoFrame("", "Looks like the pump isn't getting any power. The water must have short-circuited some of the junction "
+ "boxes. You can check which boxes are broken by selecting them.");
while (true)
{
if (Controlled.SelectedConstruction!=null &&
Controlled.SelectedConstruction.GetComponent<PowerTransfer>() != null &&
Controlled.SelectedConstruction.Condition == 0.0f)
{
brokenBox = Controlled.SelectedConstruction;
infoBox = CreateInfoFrame("", "Here's our problem: this junction box is broken. Luckily engineers are adept at fixing electrical devices - "
+ "you just need to find a spare wire and click the \"Fix\"-button to repair the box.");
break;
}
if (pump.Voltage > pump.MinVoltage) break;
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
}
if (brokenBox != null && brokenBox.ConditionPercentage > 50.0f && pump.Voltage < pump.MinVoltage)
{
yield return new WaitForSeconds(1.0f);
if (pump.Voltage < pump.MinVoltage)
{
infoBox = CreateInfoFrame("", "The pump is still not running. Check if there are more broken junction boxes between the pump and the reactor.");
}
brokenBox = null;
}
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "The pump is up and running. Wait for the water to be drained out.");
while (pump.Item.CurrentHull.WaterVolume > 1000.0f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("", "That was all there is to this tutorial! Now you should be able to handle " +
"most of the basic tasks on board the submarine.");
Completed = true;
yield return new WaitForSeconds(4.0f);
Controlled = null;
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
var cinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, Alignment.CenterLeft, Alignment.CenterRight, panDuration: 5.0f);
while (cinematic.Running)
{
yield return Controlled != null && Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
Submarine.Unload();
GameMain.MainMenuScreen.Select();
yield return CoroutineStatus.Success;
}
private bool HasItem(string itemIdentifier)
{
if (Character.Controlled == null) return false;
return Character.Controlled.Inventory.FindItemByIdentifier(itemIdentifier) != null;
}
protected IEnumerable<CoroutineStatus> KeepReactorRunning(Reactor reactor)
{
do
{
//TODO: reimplement
/*reactor.AutoTemp = true;
reactor.ShutDownTemp = 5000.0f;*/
yield return CoroutineStatus.Running;
} while (Item.ItemList.Contains(reactor.Item));
yield return CoroutineStatus.Success;
}
/// <summary>
/// keeps the enemy away from the sub until the capacitors are loaded
/// </summary>
private IEnumerable<CoroutineStatus> KeepEnemyAway(Character enemy, PowerContainer[] capacitors)
{
do
{
if (enemy == null || Character.Controlled == null) break;
//TODO: reimplement
//enemy.Health = 50.0f;
if (enemy.AIController is EnemyAIController enemyAI)
{
enemyAI.State = AIState.Idle;
}
Vector2 targetPos = Character.Controlled.WorldPosition + new Vector2(0.0f, 3000.0f);
Vector2 steering = targetPos - enemy.WorldPosition;
if (steering != Vector2.Zero) steering = Vector2.Normalize(steering);
enemy.AIController.Steering = steering;
yield return CoroutineStatus.Running;
} while (capacitors.FirstOrDefault(c => c.Charge > 0.4f) == null);
yield return CoroutineStatus.Success;
}
}
}

View File

@@ -1,9 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Tutorials
{
@@ -14,8 +13,6 @@ namespace Barotrauma.Tutorials
private float shakeAmount = 20f;
// Room 2
private MotionSensor captain_equipmentObjectiveSensor;
private ItemContainer captain_equipmentCabinet;
private Door captain_firstDoor;
private LightComponent captain_firstDoorLight;
@@ -29,7 +26,6 @@ namespace Barotrauma.Tutorials
// Submarine
private MotionSensor captain_enteredSubmarineSensor;
private Steering captain_navConsole;
private CustomInterface captain_navConsoleCustomInterface;
private Sonar captain_sonar;
private Item captain_statusMonitor;
private Character captain_security;
@@ -41,30 +37,79 @@ namespace Barotrauma.Tutorials
// Variables
private Character captain;
private string radioSpeakerName;
private LocalizedString radioSpeakerName;
private Sprite captain_steerIcon;
private Color captain_steerIconColor;
public CaptainTutorial(XElement element) : base(element)
public CaptainTutorial() : base("tutorial.captaintraining".ToIdentifier(),
new Segment(
"Captain.CommandMedic".ToIdentifier(),
"Captain.CommandMedicObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Captain.CommandMedicText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_command.webm", TextTag = "Captain.CommandMedicText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Captain.CommandMechanic".ToIdentifier(),
"Captain.CommandMechanicObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Captain.CommandMechanicText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Captain.CommandSecurity".ToIdentifier(),
"Captain.CommandSecurityObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Captain.CommandSecurityText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Captain.CommandEngineer".ToIdentifier(),
"Captain.CommandEngineerObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Captain.CommandEngineerText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Captain.Undock".ToIdentifier(),
"Captain.UndockObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Captain.UndockText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_undock.webm", TextTag = "Captain.UndockText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Captain.Navigate".ToIdentifier(),
"Captain.NavigateObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Captain.NavigateText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_navigation.webm", TextTag = "Captain.NavigateText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Captain.Dock".ToIdentifier(),
"Captain.DockObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Captain.DockText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_docking.webm", TextTag = "Captain.DockText".ToIdentifier(), Width = 450, Height = 80 }))
{ }
protected override CharacterInfo GetCharacterInfo()
{
return new CharacterInfo(
CharacterPrefab.HumanSpeciesName,
jobOrJobPrefab: new Job(
JobPrefab.Prefabs["captain"], Rand.RandSync.Unsynced, 0,
new Skill("medical".ToIdentifier(), 20),
new Skill("weapons".ToIdentifier(), 20),
new Skill("mechanical".ToIdentifier(), 20),
new Skill("electrical".ToIdentifier(), 20),
new Skill("helm".ToIdentifier(), 70)));
}
public override void Start()
protected override void Initialize()
{
base.Start();
captain = Character.Controlled;
radioSpeakerName = TextManager.Get("Tutorial.Radio.Watchman");
GameMain.GameSession.CrewManager.AllowCharacterSwitch = false;
var revolver = FindOrGiveItem(captain, "revolver");
var revolver = FindOrGiveItem(captain, "revolver".ToIdentifier());
revolver.Unequip(captain);
captain.Inventory.RemoveItem(revolver);
var captainscap =
captain.Inventory.FindItemByIdentifier("captainscap1") ??
captain.Inventory.FindItemByIdentifier("captainscap2") ??
captain.Inventory.FindItemByIdentifier("captainscap3");
captain.Inventory.FindItemByIdentifier("captainscap1".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainscap2".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainscap3".ToIdentifier());
if (captainscap != null)
{
@@ -73,22 +118,20 @@ namespace Barotrauma.Tutorials
}
var captainsuniform =
captain.Inventory.FindItemByIdentifier("captainsuniform1") ??
captain.Inventory.FindItemByIdentifier("captainsuniform2") ??
captain.Inventory.FindItemByIdentifier("captainsuniform3");
captain.Inventory.FindItemByIdentifier("captainsuniform1".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainsuniform2".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainsuniform3".ToIdentifier());
if (captainsuniform != null)
{
captainsuniform.Unequip(captain);
captain.Inventory.RemoveItem(captainsuniform);
}
var steerOrder = Order.GetPrefab("steer");
var steerOrder = OrderPrefab.Prefabs["steer"];
captain_steerIcon = steerOrder.SymbolSprite;
captain_steerIconColor = steerOrder.Color;
// Room 2
captain_equipmentObjectiveSensor = Item.ItemList.Find(i => i.HasTag("captain_equipmentobjectivesensor")).GetComponent<MotionSensor>();
captain_equipmentCabinet = Item.ItemList.Find(i => i.HasTag("captain_equipmentcabinet")).GetComponent<ItemContainer>();
captain_firstDoor = Item.ItemList.Find(i => i.HasTag("captain_firstdoor")).GetComponent<Door>();
captain_firstDoorLight = Item.ItemList.Find(i => i.HasTag("captain_firstdoorlight")).GetComponent<LightComponent>();
@@ -99,9 +142,11 @@ namespace Barotrauma.Tutorials
captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition;
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("medicaldoctor"));
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("medicaldoctor"))
{
TeamID = CharacterTeamType.Team1
};
captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor");
captain_medic.TeamID = CharacterTeamType.Team1;
captain_medic.GiveJobItems(null);
captain_medic.CanSpeak = captain_medic.AIController.Enabled = false;
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);
@@ -110,7 +155,6 @@ namespace Barotrauma.Tutorials
captain_enteredSubmarineSensor = Item.ItemList.Find(i => i.HasTag("captain_enteredsubmarinesensor")).GetComponent<MotionSensor>();
tutorial_submarineReactor = Item.ItemList.Find(i => i.HasTag("engineer_submarinereactor")).GetComponent<Reactor>();
captain_navConsole = Item.ItemList.Find(i => i.HasTag("command")).GetComponent<Steering>();
captain_navConsoleCustomInterface = Item.ItemList.Find(i => i.HasTag("command")).GetComponent<CustomInterface>();
captain_sonar = captain_navConsole.Item.GetComponent<Sonar>();
captain_statusMonitor = Item.ItemList.Find(i => i.HasTag("captain_statusmonitor"));
@@ -122,19 +166,25 @@ namespace Barotrauma.Tutorials
SetDoorAccess(tutorial_lockedDoor_1, null, false);
SetDoorAccess(tutorial_lockedDoor_2, null, false);
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("mechanic"));
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("mechanic"))
{
TeamID = CharacterTeamType.Team1
};
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "mechanic");
captain_mechanic.TeamID = CharacterTeamType.Team1;
captain_mechanic.GiveJobItems();
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("securityofficer"))
{
TeamID = CharacterTeamType.Team1
};
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "securityofficer");
captain_security.TeamID = CharacterTeamType.Team1;
captain_security.GiveJobItems();
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("engineer"))
{
TeamID = CharacterTeamType.Team1
};
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "engineer");
captain_engineer.TeamID = CharacterTeamType.Team1;
captain_engineer.GiveJobItems();
captain_mechanic.CanSpeak = captain_security.CanSpeak = captain_engineer.CanSpeak = false;
@@ -163,7 +213,7 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(2f, false);
GameMain.GameSession.CrewManager.AutoShowCrewList();
GameMain.GameSession.CrewManager.AddCharacter(captain_medic);
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Command));
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command));
do
{
yield return null;
@@ -172,13 +222,13 @@ namespace Barotrauma.Tutorials
}
while (!HasOrder(captain_medic, "follow"));
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
RemoveCompletedObjective(segments[0]);
RemoveCompletedObjective(0);
// Submarine
do { yield return null; } while (!captain_enteredSubmarineSensor.MotionDetected);
yield return new WaitForSeconds(3f, false);
captain_mechanic.AIController.Enabled = captain_security.AIController.Enabled = captain_engineer.AIController.Enabled = true;
TriggerTutorialSegment(1, GameMain.Config.KeyBindText(InputType.Command));
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command));
GameMain.GameSession.CrewManager.AddCharacter(captain_mechanic);
do
{
@@ -187,9 +237,9 @@ namespace Barotrauma.Tutorials
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_mechanic, "repairsystems", highlightColor, new Vector2(5, 5));
//HighlightOrderOption("jobspecific");
} while (!HasOrder(captain_mechanic, "repairsystems") && !HasOrder(captain_mechanic, "repairmechanical") && !HasOrder(captain_mechanic, "repairelectrical"));
RemoveCompletedObjective(segments[1]);
RemoveCompletedObjective(1);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Command));
TriggerTutorialSegment(2, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command));
GameMain.GameSession.CrewManager.AddCharacter(captain_security);
do
{
@@ -199,10 +249,13 @@ namespace Barotrauma.Tutorials
HighlightOrderOption("fireatwill");
}
while (!HasOrder(captain_security, "operateweapons"));
RemoveCompletedObjective(segments[2]);
RemoveCompletedObjective(2);
yield return new WaitForSeconds(4f, false);
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command));
TriggerTutorialSegment(3, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command));
GameMain.GameSession.CrewManager.AddCharacter(captain_engineer);
tutorial_submarineReactor.CanBeSelected = true;
//recreate autonomous objectives to make sure the engineer didn't abandon the operate reactor objective because it was not selectable
(captain_engineer.AIController as HumanAIController).ObjectiveManager.CreateAutonomousObjectives();
do
{
yield return null;
@@ -211,8 +264,7 @@ namespace Barotrauma.Tutorials
HighlightOrderOption("powerup");
}
while (!HasOrder(captain_engineer, "operatereactor", "powerup"));
RemoveCompletedObjective(segments[3]);
tutorial_submarineReactor.CanBeSelected = true;
RemoveCompletedObjective(3);
do { yield return null; } while (!tutorial_submarineReactor.IsActive); // Wait until reactor on
TriggerTutorialSegment(4);
while (ContentRunning) yield return null;
@@ -226,7 +278,7 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(1.0f, false);
} while (Submarine.MainSub.DockedTo.Any());
captain_navConsole.UseAutoDocking = false;
RemoveCompletedObjective(segments[4]);
RemoveCompletedObjective(4);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(5); // Navigate to destination
do
@@ -241,7 +293,7 @@ namespace Barotrauma.Tutorials
yield return null;
} while (captain_sonar.CurrentMode != Sonar.Mode.Active);
do { yield return null; } while (Vector2.Distance(Submarine.MainSub.WorldPosition, Level.Loaded.EndPosition) > 4000f);
RemoveCompletedObjective(segments[5]);
RemoveCompletedObjective(5);
captain_navConsole.UseAutoDocking = true;
yield return new WaitForSeconds(4f, false);
TriggerTutorialSegment(6); // Docking
@@ -250,7 +302,7 @@ namespace Barotrauma.Tutorials
//captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f);
yield return new WaitForSeconds(1.0f, false);
} while (!Submarine.MainSub.AtEndExit || !Submarine.MainSub.DockedTo.Any());
RemoveCompletedObjective(segments[6]);
RemoveCompletedObjective(6);
yield return new WaitForSeconds(3f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.GetWithVariable("Captain.Radio.Complete", "[OUTPOSTNAME]", GameMain.GameSession.EndLocation.Name), ChatMessageType.Radio, null);
SetHighlight(captain_navConsole.Item, false);

View File

@@ -1,520 +0,0 @@
/*using System.Collections.Generic;
using System.Xml.Linq;
using System;
using Microsoft.Xna.Framework;
using Barotrauma.Items.Components;
using System.Linq;
namespace Barotrauma.Tutorials
{
class ContextualTutorial : Tutorial
{
public ContextualTutorial(XElement element) : base(element)
{
//Name = "ContextualTutorial";
}
public static bool Selected = false;
private Steering navConsole;
private Reactor reactor;
private Sonar sonar;
private Vector2 subStartingPosition;
private List<Character> crew;
private Character mechanic;
private Character engineer;
private Character injuredMember = null;
private List<Pair<Character, float>> characterTimeOnSonar;
private float requiredTimeOnSonar = 5f;
private float tutorialTimer;
private bool disableTutorialOnDeficiencyFound = true;
private float floodTutorialTimer = 0.0f;
private const float floodTutorialDelay = 2.0f;
private float medicalTutorialTimer = 0.0f;
private const float medicalTutorialDelay = 2.0f;
public override void Initialize()
{
base.Initialize();
for (int i = 0; i < segments.Count; i++)
{
segments[i].IsTriggered = false;
}
characterTimeOnSonar = new List<Pair<Character, float>>();
}
public void LoadPartiallyComplete(XElement element)
{
int[] completedSegments = element.GetAttributeIntArray("completedsegments", null);
if (completedSegments == null || completedSegments.Length == 0)
{
return;
}
if (completedSegments.Length == segments.Count) // Completed all segments
{
Stop();
return;
}
for (int i = 0; i < completedSegments.Length; i++)
{
segments[completedSegments[i]].IsTriggered = true;
}
}
public void SavePartiallyComplete(XElement element)
{
XElement tutorialElement = new XElement("contextualtutorial");
tutorialElement.Add(new XAttribute("completedsegments", GetCompletedSegments()));
element.Add(tutorialElement);
}
private string GetCompletedSegments()
{
string completedSegments = string.Empty;
for (int i = 0; i < segments.Count; i++)
{
if (segments[i].IsTriggered)
{
completedSegments += i + ",";
}
}
if (completedSegments.Length > 0)
{
completedSegments = completedSegments.TrimEnd(',');
}
return completedSegments;
}
public override void Start()
{
if (!Initialized) return;
base.Start();
injuredMember = null;
activeContentSegment = null;
tutorialTimer = floodTutorialTimer = medicalTutorialTimer = 0.0f;
subStartingPosition = Vector2.Zero;
characterTimeOnSonar.Clear();
subStartingPosition = Submarine.MainSub.WorldPosition;
navConsole = Item.ItemList.Find(i => i.HasTag("command"))?.GetComponent<Steering>();
sonar = navConsole?.Item.GetComponent<Sonar>();
reactor = Item.ItemList.Find(i => i.HasTag("reactor"))?.GetComponent<Reactor>();
#if DEBUG
if (reactor == null || navConsole == null || sonar == null)
{
infoBox = CreateInfoFrame("Error", "Submarine not compatible with the tutorial:"
+ "\nReactor - " + (reactor != null ? "OK" : "Tag 'reactor' not found")
+ "\nNavigation Console - " + (navConsole != null ? "OK" : "Tag 'command' not found")
+ "\nSonar - " + (sonar != null ? "OK" : "Not found under Navigation Console"), hasButton: true);
CoroutineManager.StartCoroutine(WaitForErrorClosed());
return;
}
#endif
if (disableTutorialOnDeficiencyFound)
{
if (reactor == null || navConsole == null || sonar == null)
{
Stop();
return;
}
}
else
{
if (navConsole == null) segments[2].IsTriggered = true; // Disable navigation console usage tutorial
if (reactor == null) segments[5].IsTriggered = true; // Disable reactor usage tutorial
if (sonar == null) segments[6].IsTriggered = true; // Disable enemy on sonar tutorial
}
crew = GameMain.GameSession.CrewManager.GetCharacters().ToList();
mechanic = CrewMemberWithJob("mechanic");
engineer = CrewMemberWithJob("engineer");
Completed = true; // Trigger completed at start to prevent the contextual tutorial from automatically activating on starting new campaigns after this one
started = true;
}
#if DEBUG
private IEnumerable<object> WaitForErrorClosed()
{
while (infoBox != null) yield return null;
Stop();
}
#endif
public override void Stop()
{
base.Stop();
characterTimeOnSonar = null;
}
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (!started || ContentRunning) return;
deltaTime *= 0.5f;
for (int i = 0; i < segments.Count; i++)
{
if (segments[i].IsTriggered || HasObjective(segments[i])) continue;
if (CheckContextualTutorials(i, deltaTime)) // Found a relevant tutorial, halt finding new ones
{
break;
}
}
}
private bool CheckContextualTutorials(int index, float deltaTime)
{
switch (index)
{
case 0: // Welcome: Game Start [Text]
if (tutorialTimer < 1.0f)
{
tutorialTimer += deltaTime;
return false;
}
break;
case 1: // Command Reactor: 2 seconds after 'Welcome' dismissed and only if no command given to start reactor [Video]
if (!segments[0].IsTriggered) return false;
if (tutorialTimer < 3.0f)
{
tutorialTimer += deltaTime;
if (HasOrder("operatereactor"))
{
segments[index].IsTriggered = true;
tutorialTimer = 2.5f;
}
return false;
}
break;
case 2: // Nav Console: 2 seconds after 'Command Reactor' dismissed or if nav console is activated [Video]
if (!IsReactorPoweredUp()) return false; // Do not advance tutorial based on this segment if reactor has not been powered up
if (Character.Controlled?.SelectedConstruction != navConsole.Item)
{
if (tutorialTimer < 4.5f)
{
tutorialTimer += deltaTime;
return false;
}
}
else
{
tutorialTimer = 4.5f;
}
TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name);
return true;
case 3: // Objective: Travel ~150 meters and while sub is not flooding [Text]
if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 8000f || IsFlooding())
{
return false;
}
else // Called earlier than others due to requiring specific args
{
TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name);
return true;
}
case 4: // Flood: Hull is breached and sub is taking on water [Video]
if (!IsFlooding())
{
return false;
}
else if (floodTutorialTimer < floodTutorialDelay)
{
floodTutorialTimer += deltaTime;
return false;
}
break;
case 5: // Reactor: Player uses reactor for the first time [Video]
if (Character.Controlled?.SelectedConstruction != reactor.Item)
{
return false;
}
break;
case 6: // Enemy on Sonar: Player witnesses creature signal on sonar for 5 seconds [Video]
if (!HasEnemyOnSonarForDuration(deltaTime))
{
return false;
}
break;
case 7: // Degrading1: Any equipment degrades to 50% health or less and player has not assigned any crew to perform maintenance [Text]
if ((mechanic == null || mechanic.IsDead) && (engineer == null || engineer.IsDead)) // Both engineer and mechanic are dead or do not exist -> do not display
{
return false;
}
bool degradedEquipmentFound = false;
foreach (Item item in Item.ItemList)
{
if (!item.Repairables.Any() || item.Condition > 50.0f) continue;
degradedEquipmentFound = true;
break;
}
if (degradedEquipmentFound)
{
if (HasOrder("repairsystems", "jobspecific"))
{
segments[index].IsTriggered = true;
return false;
}
}
else
{
return false;
}
break;
case 8: // Medical: Crewmember is injured but not killed [Video]
if (injuredMember == null)
{
for (int i = 0; i < crew.Count; i++)
{
Character member = crew[i];
if (member.Vitality < member.MaxVitality && !member.IsDead)
{
injuredMember = member;
break;
}
}
return false;
}
else if (medicalTutorialTimer < medicalTutorialDelay)
{
medicalTutorialTimer += deltaTime;
return false;
}
else
{
TriggerTutorialSegment(index, new string[] { injuredMember.Info.DisplayName,
(injuredMember.Info.Gender == Gender.Male) ? TextManager.Get("PronounPossessiveMale").ToLower() : TextManager.Get("PronounPossessiveFemale").ToLower() });
return true;
}
case 9: // Approach1: Destination is within ~100m [Video]
if (Vector2.Distance(Submarine.MainSub.WorldPosition, Level.Loaded.EndPosition) > 8000f)
{
return false;
}
else
{
TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name);
return true;
}
case 10: // Approach2: Sub is docked [Text]
if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0)
{
return false;
}
break;
}
TriggerTutorialSegment(index);
return true;
}
protected override void CheckActiveObjectives(TutorialSegment objective, float deltaTime)
{
switch(objective.Id)
{
case "ReactorCommand": // Reactor commanded
if (!IsReactorPoweredUp())
{
if (!HasOrder("operatereactor")) return;
}
break;
case "NavConsole": // traveled 50 meters
if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 4000f)
{
return;
}
break;
case "Flood": // Hull breaches repaired
if (IsFlooding()) return;
break;
case "Medical":
if (injuredMember != null && !injuredMember.IsDead)
{
if (injuredMember.CharacterHealth.DroppedItem == null) return;
}
break;
case "EnemyOnSonar": // Enemy dispatched
if (HasEnemyOnSonarForDuration(deltaTime))
{
return;
}
break;
case "Degrading": // Fixed
if (mechanic != null && !mechanic.IsDead)
{
HumanAIController humanAI = mechanic.AIController as HumanAIController;
if (mechanic.CurrentOrder?.AITag != "repairsystems" || humanAI.CurrentOrderOption != "jobspecific")
{
return;
}
}
if (engineer != null && !engineer.IsDead)
{
HumanAIController humanAI = engineer.AIController as HumanAIController;
if (engineer.CurrentOrder?.AITag != "repairsystems" || humanAI.CurrentOrderOption != "jobspecific")
{
return;
}
}
break;
case "Approach1": // Wait until docked
if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0)
{
return;
}
break;
}
RemoveCompletedObjective(objective);
}
private bool IsReactorPoweredUp()
{
float load = 0.0f;
List<Connection> connections = reactor.Item.Connections;
if (connections != null && connections.Count > 0)
{
foreach (Connection connection in connections)
{
if (!connection.IsPower) continue;
foreach (Connection recipient in connection.Recipients)
{
if (!(recipient.Item is Item it)) continue;
PowerTransfer pt = it.GetComponent<PowerTransfer>();
if (pt == null) continue;
load = Math.Max(load, pt.PowerLoad);
}
}
}
return Math.Abs(load + reactor.CurrPowerConsumption) < 10;
}
private Character CrewMemberWithJob(string job)
{
job = job.ToLowerInvariant();
for (int i = 0; i < crew.Count; i++)
{
if (crew[i].Info.Job.Prefab.Identifier.ToLowerInvariant() == job) return crew[i];
}
return null;
}
private bool HasOrder(string aiTag, string option = null)
{
for (int i = 0; i < crew.Count; i++)
{
if (crew[i].CurrentOrder?.AITag == aiTag)
{
if (option == null)
{
return true;
}
else
{
HumanAIController humanAI = crew[i].AIController as HumanAIController;
return humanAI.CurrentOrderOption == option;
}
}
}
return false;
}
private bool IsFlooding()
{
foreach (Gap gap in Gap.GapList)
{
if (gap.ConnectedWall == null || gap.IsRoomToRoom) continue;
if (gap.ConnectedDoor != null || gap.Open <= 0.0f) continue;
if (gap.Submarine == null) continue;
if (gap.Submarine.IsOutpost) continue;
if (gap.Submarine != Submarine.MainSub) continue;
if (gap.FlowTargetHull == null || gap.FlowTargetHull.WaterPercentage <= 0.0f) continue;
return true;
}
return false;
}
private bool HasEnemyOnSonarForDuration(float deltaTime)
{
foreach (Character c in Character.CharacterList)
{
if (c.AnimController.CurrentHull != null || !c.Enabled || !(c.AIController is EnemyAIController)) continue;
if (sonar.DetectSubmarineWalls && c.AnimController.CurrentHull == null && sonar.Item.CurrentHull != null) continue;
if (Vector2.DistanceSquared(c.WorldPosition, sonar.Item.WorldPosition) > sonar.Range * sonar.Range)
{
for (int i = 0; i < characterTimeOnSonar.Count; i++)
{
if (characterTimeOnSonar[i].First == c)
{
characterTimeOnSonar.RemoveAt(i);
break;
}
}
continue;
}
Pair<Character, float> pair = characterTimeOnSonar.Find(ct => ct.First == c);
if (pair != null)
{
pair.Second += deltaTime;
}
else
{
characterTimeOnSonar.Add(new Pair<Character, float>(c, deltaTime));
}
}
return characterTimeOnSonar.Find(ct => ct.Second >= requiredTimeOnSonar && !ct.First.IsDead) != null;
}
protected override void TriggerTutorialSegment(int index, params object[] args)
{
base.TriggerTutorialSegment(index, args);
for (int i = 0; i < segments.Count; i++)
{
if (!segments[i].IsTriggered) return;
}
CoroutineManager.StartCoroutine(WaitToStop()); // Completed
}
private IEnumerable<object> WaitToStop()
{
while (ContentRunning) yield return null;
Stop();
}
}
}*/

View File

@@ -4,8 +4,6 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
@@ -15,7 +13,7 @@ namespace Barotrauma.Tutorials
private float shakeTimer = 1f;
private float shakeAmount = 20f;
private string radioSpeakerName;
private LocalizedString radioSpeakerName;
private Character doctor;
private ItemContainer doctor_suppliesCabinet;
@@ -40,14 +38,66 @@ namespace Barotrauma.Tutorials
private Sprite doctor_firstAidIcon;
private Color doctor_firstAidIconColor;
public DoctorTutorial(XElement element) : base(element)
{
}
public override void Start()
{
base.Start();
var firstAidOrder = Order.GetPrefab("requestfirstaid");
public DoctorTutorial() : base("tutorial.medicaldoctortraining".ToIdentifier(),
new Segment(
"Doctor.Supplies".ToIdentifier(),
"Doctor.SuppliesObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Doctor.SuppliesText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Doctor.OpenMedicalInterface".ToIdentifier(),
"Doctor.OpenMedicalInterfaceObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Doctor.OpenMedicalInterfaceText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_medinterface1.webm", TextTag = "Doctor.OpenMedicalInterfaceText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Doctor.FirstAidSelf".ToIdentifier(),
"Doctor.FirstAidSelfObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Doctor.FirstAidSelfText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_medinterface1.webm", TextTag = "Doctor.FirstAidSelfText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Doctor.Medbay".ToIdentifier(),
"Doctor.MedbayObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Doctor.MedbayText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_command.webm", TextTag = "Doctor.MedbayText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Doctor.TreatBurns".ToIdentifier(),
"Doctor.TreatBurnsObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Doctor.TreatBurnsText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_medinterface2.webm", TextTag = "Doctor.TreatBurnsText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Doctor.CPR".ToIdentifier(),
"Doctor.CPRObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Doctor.CPRText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_cpr.webm", TextTag = "Doctor.CPRText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Doctor.Submarine".ToIdentifier(),
"Doctor.SubmarineObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Doctor.SubmarineText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }))
{ }
protected override CharacterInfo GetCharacterInfo()
{
return new CharacterInfo(
CharacterPrefab.HumanSpeciesName,
jobOrJobPrefab: new Job(
JobPrefab.Prefabs["medicaldoctor"], Rand.RandSync.Unsynced, 0,
new Skill("medical".ToIdentifier(), 70),
new Skill("weapons".ToIdentifier(), 20),
new Skill("mechanical".ToIdentifier(), 20),
new Skill("electrical".ToIdentifier(), 20),
new Skill("helm".ToIdentifier(), 20)));
}
protected override void Initialize()
{
var firstAidOrder = OrderPrefab.Prefabs["requestfirstaid"];
doctor_firstAidIcon = firstAidOrder.SymbolSprite;
doctor_firstAidIconColor = firstAidOrder.Color;
@@ -55,19 +105,19 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
doctor = Character.Controlled;
var bandages = FindOrGiveItem(doctor, "antibleeding1");
var bandages = FindOrGiveItem(doctor, "antibleeding1".ToIdentifier());
bandages.Unequip(doctor);
doctor.Inventory.RemoveItem(bandages);
var syringegun = FindOrGiveItem(doctor, "syringegun");
var syringegun = FindOrGiveItem(doctor, "syringegun".ToIdentifier());
syringegun.Unequip(doctor);
doctor.Inventory.RemoveItem(syringegun);
var antibiotics = FindOrGiveItem(doctor, "antibiotics");
var antibiotics = FindOrGiveItem(doctor, "antibiotics".ToIdentifier());
antibiotics.Unequip(doctor);
doctor.Inventory.RemoveItem(antibiotics);
var morphine = FindOrGiveItem(doctor, "antidama1");
var morphine = FindOrGiveItem(doctor, "antidama1".ToIdentifier());
morphine.Unequip(doctor);
doctor.Inventory.RemoveItem(morphine);
@@ -78,36 +128,47 @@ namespace Barotrauma.Tutorials
var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull;
medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull;
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("assistant"));
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("assistant"))
{
TeamID = CharacterTeamType.Team1
};
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
patient1.TeamID = CharacterTeamType.Team1;
patient1.GiveJobItems(null);
patient1.CanSpeak = false;
patient1.Params.Health.BurnReduction = 0;
patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 15.0f) }, stun: 0, playSound: false);
patient1.AIController.Enabled = false;
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("assistant"));
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("assistant"))
{
TeamID = CharacterTeamType.Team1
};
patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2");
patient2.TeamID = CharacterTeamType.Team1;
patient2.GiveJobItems(null);
patient2.CanSpeak = false;
patient2.AIController.Enabled = false;
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("engineer"))
{
TeamID = CharacterTeamType.Team1
};
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
subPatient1.TeamID = CharacterTeamType.Team1;
subPatient1.Params.Health.BurnReduction = 0;
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient1);
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("securityofficer"));
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
subPatient2.TeamID = CharacterTeamType.Team1;
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient2);
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("engineer"))
{
TeamID = CharacterTeamType.Team1
};
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
subPatient3.TeamID = CharacterTeamType.Team1;
subPatient3.Params.Health.BurnReduction = 0;
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient3);
@@ -196,7 +257,7 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(2.0f);
}*/
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Deselect), GameMain.Config.KeyBindText(InputType.ToggleInventory)); // Medical supplies objective
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Medical supplies objective
do
{
@@ -215,24 +276,24 @@ namespace Barotrauma.Tutorials
}
}
yield return null;
} while (doctor.Inventory.FindItemByIdentifier("antidama1") == null); // Wait until looted
} while (doctor.Inventory.FindItemByIdentifier("antidama1".ToIdentifier()) == null); // Wait until looted
yield return new WaitForSeconds(1.0f, false);
SetHighlight(doctor_suppliesCabinet.Item, false);
RemoveCompletedObjective(segments[0]);
RemoveCompletedObjective(0);
yield return new WaitForSeconds(1.0f, false);
// 2nd tutorial segment, treat self -------------------------------------------------------------------------
TriggerTutorialSegment(1, GameMain.Config.KeyBindText(InputType.Health)); // Open health interface
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)); // Open health interface
while (CharacterHealth.OpenHealthWindow == null)
{
doctor.CharacterHealth.HealthBarPulsateTimer = 1.0f;
yield return null;
}
yield return null;
RemoveCompletedObjective(segments[1]);
RemoveCompletedObjective(1);
yield return new WaitForSeconds(1.0f, false);
TriggerTutorialSegment(2); //Treat self
while (doctor.CharacterHealth.GetAfflictionStrength("damage") > 0.01f)
@@ -243,13 +304,13 @@ namespace Barotrauma.Tutorials
}
else
{
HighlightInventorySlot(doctor.Inventory, "antidama1", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(doctor.Inventory, "antidama1".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return null;
}
RemoveCompletedObjective(segments[2]);
RemoveCompletedObjective(2);
SetDoorAccess(doctor_firstDoor, doctor_firstDoorLight, true);
while (CharacterHealth.OpenHealthWindow != null)
@@ -260,10 +321,10 @@ namespace Barotrauma.Tutorials
// treat patient --------------------------------------------------------------------------------------------
//patient 1 requests first aid
var newOrder = new Order(Order.GetPrefab("requestfirstaid"), patient1.CurrentHull, null, orderGiver: patient1);
var newOrder = new Order(OrderPrefab.Prefabs["requestfirstaid"], patient1.CurrentHull, null, orderGiver: patient1);
doctor.AddActiveObjectiveEntity(patient1, doctor_firstAidIcon, doctor_firstAidIconColor);
//GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(patient1.Name, newOrder.GetChatMessage("", patient1.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order, null);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(patient1.Name, newOrder.GetChatMessage("", patient1.CurrentHull?.DisplayName?.Value, givingOrderToSelf: false), ChatMessageType.Order, null);
while (doctor.CurrentHull != patient1.CurrentHull)
{
@@ -281,9 +342,9 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(3.0f, false);
patient1.AIController.Enabled = true;
doctor.RemoveActiveObjectiveEntity(patient1);
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command)); // Get the patient to medbay
TriggerTutorialSegment(3, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command)); // Get the patient to medbay
while (patient1.GetCurrentOrderWithTopPriority()?.Order?.Identifier != "follow")
while (patient1.GetCurrentOrderWithTopPriority()?.Identifier != "follow")
{
// TODO: Rework order highlighting for new command UI
// GameMain.GameSession.CrewManager.HighlightOrderButton(patient1, "follow", highlightColor, new Vector2(5, 5));
@@ -296,14 +357,14 @@ namespace Barotrauma.Tutorials
{
yield return new WaitForSeconds(1.0f, false);
}
RemoveCompletedObjective(segments[3]);
RemoveCompletedObjective(3);
SetHighlight(doctor_medBayCabinet.Item, true);
SetDoorAccess(doctor_thirdDoor, doctor_thirdDoorLight, true);
patient1.CharacterHealth.UseHealthWindow = true;
yield return new WaitForSeconds(2.0f, false);
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Health)); // treat burns
TriggerTutorialSegment(4, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)); // treat burns
do
{
@@ -322,7 +383,7 @@ namespace Barotrauma.Tutorials
}
}
yield return null;
} while (doctor.Inventory.FindItemByIdentifier("antibleeding1") == null); // Wait until looted
} while (doctor.Inventory.FindItemByIdentifier("antibleeding1".ToIdentifier()) == null); // Wait until looted
SetHighlight(doctor_medBayCabinet.Item, false);
SetHighlight(patient1, true);
@@ -334,12 +395,12 @@ namespace Barotrauma.Tutorials
}
else
{
HighlightInventorySlot(doctor.Inventory, "antibleeding1", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(doctor.Inventory, "antibleeding1".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return null;
}
RemoveCompletedObjective(segments[4]);
RemoveCompletedObjective(4);
SetHighlight(patient1, false);
yield return new WaitForSeconds(1.0f, false);
@@ -350,10 +411,10 @@ namespace Barotrauma.Tutorials
//patient calls for help
//patient2.CanSpeak = true;
yield return new WaitForSeconds(2.0f, false);
newOrder = new Order(Order.GetPrefab("requestfirstaid"), patient2.CurrentHull, null, orderGiver: patient2);
newOrder = new Order(OrderPrefab.Prefabs["requestfirstaid"], patient2.CurrentHull, null, orderGiver: patient2);
doctor.AddActiveObjectiveEntity(patient2, doctor_firstAidIcon, doctor_firstAidIconColor);
//GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(patient2.Name, newOrder.GetChatMessage("", patient1.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order, null);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(patient2.Name, newOrder.GetChatMessage("", patient1.CurrentHull?.DisplayName?.Value, givingOrderToSelf: false), ChatMessageType.Order, null);
patient2.AIController.Enabled = true;
patient2.Oxygen = -50;
CoroutineManager.StartCoroutine(KeepPatientAlive(patient2), "KeepPatient2Alive");
@@ -365,7 +426,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!tutorial_upperFinalDoor.IsOpen);
yield return new WaitForSeconds(2.0f, false);
TriggerTutorialSegment(5, GameMain.Config.KeyBindText(InputType.Health)); // perform CPR
TriggerTutorialSegment(5, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)); // perform CPR
SetHighlight(patient2, true);
while (patient2.IsUnconscious)
{
@@ -380,7 +441,7 @@ namespace Barotrauma.Tutorials
}
yield return null;
}
RemoveCompletedObjective(segments[5]);
RemoveCompletedObjective(5);
SetHighlight(patient2, false);
doctor.RemoveActiveObjectiveEntity(patient2);
CoroutineManager.StopCoroutines("KeepPatient2Alive");
@@ -399,7 +460,7 @@ namespace Barotrauma.Tutorials
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Doctor.Radio.EnteredSub"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(3.0f, false);
TriggerTutorialSegment(6, GameMain.Config.KeyBindText(InputType.Health)); // give treatment to anyone in need
TriggerTutorialSegment(6, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)); // give treatment to anyone in need
foreach (var patient in subPatients)
{
@@ -421,8 +482,8 @@ namespace Barotrauma.Tutorials
if (!patientCalledHelp[i] && Timing.TotalTime > subEnterTime + 60 * (i + 1))
{
doctor.AddActiveObjectiveEntity(subPatients[i], doctor_firstAidIcon, doctor_firstAidIconColor);
newOrder = new Order(Order.GetPrefab("requestfirstaid"), subPatients[i].CurrentHull, null, orderGiver: subPatients[i]);
string message = newOrder.GetChatMessage("", subPatients[i].CurrentHull?.DisplayName, givingOrderToSelf: false);
newOrder = new Order(OrderPrefab.Prefabs["requestfirstaid"], subPatients[i].CurrentHull, null, orderGiver: subPatients[i]);
string message = newOrder.GetChatMessage("", subPatients[i].CurrentHull?.DisplayName?.Value, givingOrderToSelf: false);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(subPatients[i].Name, message, ChatMessageType.Order, null);
patientCalledHelp[i] = true;
}
@@ -435,7 +496,7 @@ namespace Barotrauma.Tutorials
}
yield return new WaitForSeconds(1.0f, false);
}
RemoveCompletedObjective(segments[6]);
RemoveCompletedObjective(6);
foreach (var patient in subPatients)
{
SetHighlight(patient, false);

View File

@@ -1,44 +0,0 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
class EditorTutorial : Tutorial
{
public EditorTutorial(XElement element)
: base (element)
{
}
public override IEnumerable<CoroutineStatus> UpdateState()
{
/*infoBox = CreateInfoFrame("Use the mouse wheel to zoom in and out, and WASD to move the camera around.", true);
while (infoBox != null)
{
yield return CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("Press \"Structure\" at the left side of the screen to start placing some walls.");
while (GameMain.SubEditorScreen.SelectedTab != (int)MapEntityCategory.Structure)
{
yield return CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("Select \"topwall\" from the list.", true);
while (MapEntityPrefab.Selected == null || MapEntityPrefab.Selected.Name != "topwall")
{
yield return CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("You can now create a horizontal wall by clicking and dragging. When you're done, right click to stop creating walls.");*/
yield return CoroutineStatus.Success;
}
}
}

View File

@@ -25,20 +25,17 @@ namespace Barotrauma.Tutorials
private LightComponent engineer_firstDoorLight;
// Room 3
private MotionSensor engineer_reactorObjectiveSensor;
private Powered tutorial_oxygenGenerator;
private Reactor engineer_reactor;
private Door engineer_secondDoor;
private LightComponent engineer_secondDoorLight;
// Room 4
private MotionSensor engineer_repairJunctionBoxObjectiveSensor;
private Item engineer_brokenJunctionBox;
private Door engineer_thirdDoor;
private LightComponent engineer_thirdDoorLight;
// Room 5
private MotionSensor engineer_disconnectedJunctionBoxObjectiveSensor;
private PowerTransfer[] engineer_disconnectedJunctionBoxes;
private ConnectionPanel[] engineer_disconnectedConnectionPanels;
private Item engineer_wire_1;
@@ -62,7 +59,7 @@ namespace Barotrauma.Tutorials
private Reactor engineer_submarineReactor;
// Variables
private string radioSpeakerName;
private LocalizedString radioSpeakerName;
private Character engineer;
private int[] reactorLoads = new int[5] { 1500, 3000, 2000, 5000, 3500 };
private float reactorLoadChangeTime = 2f;
@@ -75,27 +72,74 @@ namespace Barotrauma.Tutorials
private Color engineer_reactorIconColor;
private bool wiringActive = false;
public EngineerTutorial(XElement element) : base(element)
{
public EngineerTutorial() : base("tutorial.engineertraining".ToIdentifier(),
new Segment(
"Mechanic.Equipment".ToIdentifier(),
"Mechanic.EquipmentObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.EquipmentText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Engineer.Reactor".ToIdentifier(),
"Engineer.ReactorObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Engineer.ReactorText".ToIdentifier(), Width = 700, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_reactor.webm", TextTag = "Engineer.ReactorText".ToIdentifier(), Width = 700, Height = 80 }),
new Segment(
"Engineer.OperateReactor".ToIdentifier(),
"Engineer.OperateReactorObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Engineer.OperateReactorText".ToIdentifier(), Width = 700, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_reactor.webm", TextTag = "Engineer.ReactorText".ToIdentifier(), Width = 700, Height = 80 }),
new Segment(
"Engineer.RepairJunctionBox".ToIdentifier(),
"Engineer.RepairJunctionBoxObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Engineer.RepairJunctionBoxText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Engineer.WireJunctionBoxes".ToIdentifier(),
"Engineer.WireJunctionBoxesObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Engineer.WireJunctionBoxesText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_wiring.webm", TextTag = "Engineer.WireJunctionBoxesText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Engineer.RepairElectricalRoom".ToIdentifier(),
"Engineer.RepairElectricalRoomObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Engineer.RepairElectricalRoomText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Engineer.PowerUpReactor".ToIdentifier(),
"Engineer.PowerUpReactorObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Engineer.PowerUpReactorText".ToIdentifier(), Width = 700, Height = 80, Anchor = Anchor.Center }))
{ }
protected override CharacterInfo GetCharacterInfo()
{
return new CharacterInfo(
CharacterPrefab.HumanSpeciesName,
jobOrJobPrefab: new Job(
JobPrefab.Prefabs["engineer"], Rand.RandSync.Unsynced, 0,
new Skill("medical".ToIdentifier(), 0),
new Skill("weapons".ToIdentifier(), 0),
new Skill("mechanical".ToIdentifier(), 20),
new Skill("electrical".ToIdentifier(), 60),
new Skill("helm".ToIdentifier(), 0)));
}
public override void Start()
protected override void Initialize()
{
base.Start();
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
engineer = Character.Controlled;
var toolbelt = FindOrGiveItem(engineer, "toolbelt");
var toolbelt = FindOrGiveItem(engineer, "toolbelt".ToIdentifier());
toolbelt.Unequip(engineer);
engineer.Inventory.RemoveItem(toolbelt);
var repairOrder = Order.GetPrefab("repairsystems");
var repairOrder = OrderPrefab.Prefabs["repairsystems"];
engineer_repairIcon = repairOrder.SymbolSprite;
engineer_repairIconColor = repairOrder.Color;
var reactorOrder = Order.GetPrefab("operatereactor");
var reactorOrder = OrderPrefab.Prefabs["operatereactor"];
engineer_reactorIcon = reactorOrder.SymbolSprite;
engineer_reactorIconColor = reactorOrder.Color;
@@ -122,7 +166,6 @@ namespace Barotrauma.Tutorials
SetDoorAccess(engineer_firstDoor, engineer_firstDoorLight, false);
// Room 3
engineer_reactorObjectiveSensor = Item.ItemList.Find(i => i.HasTag("engineer_reactorobjectivesensor")).GetComponent<MotionSensor>();
tutorial_oxygenGenerator = Item.ItemList.Find(i => i.HasTag("tutorial_oxygengenerator")).GetComponent<OxygenGenerator>();
engineer_reactor = Item.ItemList.Find(i => i.HasTag("engineer_reactor")).GetComponent<Reactor>();
engineer_reactor.FireDelay = engineer_reactor.MeltdownDelay = float.PositiveInfinity;
@@ -136,7 +179,6 @@ namespace Barotrauma.Tutorials
SetDoorAccess(engineer_secondDoor, engineer_secondDoorLight, false);
// Room 4
engineer_repairJunctionBoxObjectiveSensor = Item.ItemList.Find(i => i.HasTag("engineer_repairjunctionboxobjectivesensor")).GetComponent<MotionSensor>();
engineer_brokenJunctionBox = Item.ItemList.Find(i => i.HasTag("engineer_brokenjunctionbox"));
engineer_thirdDoor = Item.ItemList.Find(i => i.HasTag("engineer_thirddoor")).GetComponent<Door>();
engineer_thirdDoorLight = Item.ItemList.Find(i => i.HasTag("engineer_thirddoorlight")).GetComponent<LightComponent>();
@@ -147,8 +189,6 @@ namespace Barotrauma.Tutorials
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, false);
// Room 5
engineer_disconnectedJunctionBoxObjectiveSensor = Item.ItemList.Find(i => i.HasTag("engineer_disconnectedjunctionboxobjectivesensor")).GetComponent<MotionSensor>();
engineer_disconnectedJunctionBoxes = new PowerTransfer[4];
engineer_disconnectedConnectionPanels = new ConnectionPanel[4];
@@ -208,7 +248,7 @@ namespace Barotrauma.Tutorials
public override IEnumerable<CoroutineStatus> UpdateState()
{
while (GameMain.Instance.LoadingScreenOpen) yield return null;
while (GameMain.Instance.LoadingScreenOpen) { yield return null; }
// Room 1
SoundPlayer.PlayDamageSound("StructureBlunt", 10, Character.Controlled.WorldPosition);
@@ -235,7 +275,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_equipmentObjectiveSensor.MotionDetected);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Equipment"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(0.5f, false);
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Deselect), GameMain.Config.KeyBindText(InputType.ToggleInventory)); // Retrieve equipment
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Retrieve equipment
bool firstSlotRemoved = false;
bool secondSlotRemoved = false;
bool thirdSlotRemoved = false;
@@ -276,7 +316,7 @@ namespace Barotrauma.Tutorials
yield return null;
} while (!engineer_equipmentCabinet.Inventory.IsEmpty()); // Wait until looted
RemoveCompletedObjective(segments[0]);
RemoveCompletedObjective(0);
SetHighlight(engineer_equipmentCabinet.Item, false);
SetHighlight(engineer_reactor.Item, true);
SetDoorAccess(engineer_firstDoor, engineer_firstDoorLight, true);
@@ -302,7 +342,7 @@ namespace Barotrauma.Tutorials
if (IsSelectedItem(engineer_reactor.Item) && engineer_reactor.Item.OwnInventory.visualSlots != null)
{
engineer_reactor.AutoTemp = false;
HighlightInventorySlot(engineer.Inventory, "fuelrod", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(engineer.Inventory, "fuelrod".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
for (int i = 0; i < engineer_reactor.Item.OwnInventory.visualSlots.Length; i++)
{
@@ -311,7 +351,7 @@ namespace Barotrauma.Tutorials
}
yield return null;
} while (engineer_reactor.AvailableFuel == 0);
RemoveCompletedObjective(segments[1]);
RemoveCompletedObjective(1);
TriggerTutorialSegment(2);
CoroutineManager.StartCoroutine(ReactorOperatedProperly());
do
@@ -354,7 +394,7 @@ namespace Barotrauma.Tutorials
} while (wait > 0.0f);
engineer.SelectedConstruction = null;
engineer_reactor.CanBeSelected = false;
RemoveCompletedObjective(segments[2]);
RemoveCompletedObjective(2);
SetHighlight(engineer_reactor.Item, false);
SetHighlight(engineer_brokenJunctionBox, true);
SetDoorAccess(engineer_secondDoor, engineer_secondDoorLight, true);
@@ -363,12 +403,12 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_secondDoor.IsOpen);
yield return new WaitForSeconds(1f, false);
Repairable repairableJunctionBoxComponent = engineer_brokenJunctionBox.GetComponent<Repairable>();
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Select)); // Repair the junction box
TriggerTutorialSegment(3, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select)); // Repair the junction box
do
{
if (!engineer.HasEquippedItem("screwdriver"))
if (!engineer.HasEquippedItem("screwdriver".ToIdentifier()))
{
HighlightInventorySlot(engineer.Inventory, "screwdriver", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(engineer.Inventory, "screwdriver".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
else if (IsSelectedItem(engineer_brokenJunctionBox) && repairableJunctionBoxComponent.CurrentFixer == null)
{
@@ -380,7 +420,7 @@ namespace Barotrauma.Tutorials
yield return null;
} while (repairableJunctionBoxComponent.IsBelowRepairThreshold); // Wait until repaired
SetHighlight(engineer_brokenJunctionBox, false);
RemoveCompletedObjective(segments[3]);
RemoveCompletedObjective(3);
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, true);
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
{
@@ -391,14 +431,14 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_thirdDoor.IsOpen);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.FaultyWiring"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Use), GameMain.Config.KeyBindText(InputType.Deselect)); // Connect the junction boxes
TriggerTutorialSegment(4, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Use), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Connect the junction boxes
do { CheckGhostWires(); HandleJunctionBoxWiringHighlights(); yield return null; } while (engineer_workingPump.Voltage < engineer_workingPump.MinVoltage); // Wait until connected all the way to the pump
CheckGhostWires();
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
{
SetHighlight(engineer_disconnectedJunctionBoxes[i].Item, false);
}
RemoveCompletedObjective(segments[4]);
RemoveCompletedObjective(4);
do { yield return null; } while (engineer_workingPump.Item.CurrentHull.WaterPercentage > waterVolumeBeforeOpening); // Wait until drained
wiringActive = false;
SetDoorAccess(engineer_fourthDoor, engineer_fourthDoorLight, true);
@@ -424,7 +464,7 @@ namespace Barotrauma.Tutorials
// Remove highlights when each individual machine is repaired
do { CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3); yield return null; } while (repairableJunctionBoxComponent1.IsBelowRepairThreshold || repairableJunctionBoxComponent2.IsBelowRepairThreshold || repairableJunctionBoxComponent3.IsBelowRepairThreshold);
CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3);
RemoveCompletedObjective(segments[5]);
RemoveCompletedObjective(5);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(6); // Powerup reactor
@@ -433,7 +473,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!IsReactorPoweredUp(engineer_submarineReactor)); // Wait until ~matches load
engineer.RemoveActiveObjectiveEntity(engineer_submarineReactor.Item);
SetHighlight(engineer_submarineReactor.Item, false);
RemoveCompletedObjective(segments[6]);
RemoveCompletedObjective(6);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Complete"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(4f, false);
@@ -472,10 +512,10 @@ namespace Barotrauma.Tutorials
tutorial_oxygenGenerator.PowerConsumption = reactorLoads[i];
while (timer > 0)
{
yield return new WaitForSeconds(0.1f, false);
if (IsReactorPoweredUp(engineer_reactor))
yield return CoroutineStatus.Running;
if (CoroutineManager.DeltaTime > 0.0f && IsReactorPoweredUp(engineer_reactor))
{
timer -= 0.1f;
timer -= CoroutineManager.DeltaTime;
}
}
}
@@ -516,9 +556,9 @@ namespace Barotrauma.Tutorials
{
Item selected = engineer.SelectedConstruction;
if (!engineer.HasEquippedItem("screwdriver"))
if (!engineer.HasEquippedItem("screwdriver".ToIdentifier()))
{
HighlightInventorySlot(engineer.Inventory, "screwdriver", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(engineer.Inventory, "screwdriver".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
}
int selectedIndex = -1;
@@ -537,9 +577,9 @@ namespace Barotrauma.Tutorials
wiringActive = selectedIndex != -1;
if (!engineer.HasEquippedItem("wire"))
if (!engineer.HasEquippedItem("wire".ToIdentifier()))
{
HighlightInventorySlotWithTag(engineer.Inventory, "wire", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlotWithTag(engineer.Inventory, "wire".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
}
else
{

View File

@@ -69,33 +69,106 @@ namespace Barotrauma.Tutorials
// Variables
private const float waterVolumeBeforeOpening = 15f;
private string radioSpeakerName;
private LocalizedString radioSpeakerName;
private Character mechanic;
private Sprite mechanic_repairIcon;
private Color mechanic_repairIconColor;
private Sprite mechanic_weldIcon;
public MechanicTutorial(XElement element) : base(element)
{
public MechanicTutorial() : base("tutorial.mechanictraining".ToIdentifier(),
new Segment(
"Mechanic.OpenDoor".ToIdentifier(),
"Mechanic.OpenDoorObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.OpenDoorText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Mechanic.Equipment".ToIdentifier(),
"Mechanic.EquipmentObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Mechanic.EquipmentText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_inventory.webm", TextTag = "Mechanic.EquipmentText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Mechanic.Welding".ToIdentifier(),
"Mechanic.WeldingObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Mechanic.WeldingText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_equip.webm", TextTag = "Mechanic.WeldingText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Mechanic.Drain".ToIdentifier(),
"Mechanic.DrainObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.DrainText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Mechanic.Deconstruct".ToIdentifier(),
"Mechanic.DeconstructObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Mechanic.DeconstructText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_deconstruct.webm", TextTag = "Mechanic.DeconstructText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Mechanic.Fabricate".ToIdentifier(),
"Mechanic.FabricateObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Mechanic.FabricateText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_fabricate.webm", TextTag = "Mechanic.FabricateText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Mechanic.Extinguisher".ToIdentifier(),
"Mechanic.ExtinguisherObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.ExtinguisherText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Mechanic.DropExtinguisher".ToIdentifier(),
"Mechanic.DropExtinguisherObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.DropExtinguisherText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Mechanic.Diving".ToIdentifier(),
"Mechanic.DivingObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.DivingText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Mechanic.RepairPump".ToIdentifier(),
"Mechanic.RepairPumpObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.RepairPumpText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Mechanic.RepairSubmarine".ToIdentifier(),
"Mechanic.RepairSubmarineObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.RepairSubmarineText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"tutorial.laddertitle".ToIdentifier(),
"tutorial.laddertitle".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "tutorial.ladderdescription".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }))
{ }
protected override CharacterInfo GetCharacterInfo()
{
return new CharacterInfo(
CharacterPrefab.HumanSpeciesName,
jobOrJobPrefab: new Job(
JobPrefab.Prefabs["mechanic"], Rand.RandSync.Unsynced, 0,
new Skill("medical".ToIdentifier(), 0),
new Skill("weapons".ToIdentifier(), 0),
new Skill("mechanical".ToIdentifier(), 50),
new Skill("electrical".ToIdentifier(), 20),
new Skill("helm".ToIdentifier(), 0)));
}
public override void Start()
protected override void Initialize()
{
base.Start();
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
mechanic = Character.Controlled;
var toolbelt = FindOrGiveItem(mechanic, "toolbelt");
var toolbelt = FindOrGiveItem(mechanic, "toolbelt".ToIdentifier());
toolbelt.Unequip(mechanic);
mechanic.Inventory.RemoveItem(toolbelt);
var crowbar = FindOrGiveItem(mechanic, "crowbar");
var crowbar = FindOrGiveItem(mechanic, "crowbar".ToIdentifier());
crowbar.Unequip(mechanic);
mechanic.Inventory.RemoveItem(crowbar);
var repairOrder = Order.GetPrefab("repairsystems");
var repairOrder = OrderPrefab.Prefabs["repairsystems"];
mechanic_repairIcon = repairOrder.SymbolSprite;
mechanic_repairIconColor = repairOrder.Color;
mechanic_weldIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(1, 256, 127, 127), new Vector2(0.5f, 0.5f));
@@ -239,24 +312,25 @@ namespace Barotrauma.Tutorials
}
yield return new WaitForSeconds(2.5f, false);
mechanic_fabricator.RemoveFabricationRecipes(new List<string>() { "extinguisher", "wrench", "weldingtool", "weldingfuel", "divingmask", "railgunshell", "nuclearshell", "uex", "harpoongun" });
mechanic_fabricator.RemoveFabricationRecipes(allowedIdentifiers:
new[] { "extinguisher", "wrench", "weldingtool", "weldingfuel", "divingmask", "railgunshell", "nuclearshell", "uex", "harpoongun" }.ToIdentifiers());
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.WakeUp"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2.5f, false);
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Up), GameMain.Config.KeyBindText(InputType.Left), GameMain.Config.KeyBindText(InputType.Down), GameMain.Config.KeyBindText(InputType.Right), GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Select)); // Open door objective
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Up), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Left), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Down), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Right), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select)); // Open door objective
yield return new WaitForSeconds(0.0f, false);
SetDoorAccess(mechanic_firstDoor, mechanic_firstDoorLight, true);
SetHighlight(mechanic_firstDoor.Item, true);
do { yield return null; } while (!mechanic_firstDoor.IsOpen);
SetHighlight(mechanic_firstDoor.Item, false);
yield return new WaitForSeconds(1.5f, false);
RemoveCompletedObjective(segments[0]);
RemoveCompletedObjective(0);
// Room 2
yield return new WaitForSeconds(0.0f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Equipment"), ChatMessageType.Radio, null);
do { yield return null; } while (!mechanic_equipmentObjectiveSensor.MotionDetected);
TriggerTutorialSegment(1, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Deselect), GameMain.Config.KeyBindText(InputType.ToggleInventory)); // Equipment & inventory objective
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Equipment & inventory objective
SetHighlight(mechanic_equipmentCabinet.Item, true);
bool firstSlotRemoved = false;
bool secondSlotRemoved = false;
@@ -290,35 +364,35 @@ namespace Barotrauma.Tutorials
}
yield return null;
} while (mechanic.Inventory.FindItemByIdentifier("divingmask") == null || mechanic.Inventory.FindItemByIdentifier("weldingtool") == null || mechanic.Inventory.FindItemByIdentifier("wrench") == null); // Wait until looted
} while (mechanic.Inventory.FindItemByIdentifier("divingmask".ToIdentifier()) == null || mechanic.Inventory.FindItemByIdentifier("weldingtool".ToIdentifier()) == null || mechanic.Inventory.FindItemByIdentifier("wrench".ToIdentifier()) == null); // Wait until looted
SetHighlight(mechanic_equipmentCabinet.Item, false);
yield return new WaitForSeconds(1.5f, false);
RemoveCompletedObjective(segments[1]);
RemoveCompletedObjective(1);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Breach"), ChatMessageType.Radio, null);
// Room 3
do { yield return null; } while (!mechanic_weldingObjectiveSensor.MotionDetected);
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Aim), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.ToggleInventory)); // Welding objective
TriggerTutorialSegment(2, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Welding objective
do
{
if (!mechanic.HasEquippedItem("divingmask"))
if (!mechanic.HasEquippedItem("divingmask".ToIdentifier()))
{
HighlightInventorySlot(mechanic.Inventory, "divingmask", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "divingmask".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
if (!mechanic.HasEquippedItem("weldingtool"))
if (!mechanic.HasEquippedItem("weldingtool".ToIdentifier()))
{
HighlightInventorySlot(mechanic.Inventory, "weldingtool", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "weldingtool".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return null;
} while (!mechanic.HasEquippedItem("divingmask") || !mechanic.HasEquippedItem("weldingtool")); // Wait until equipped
} while (!mechanic.HasEquippedItem("divingmask".ToIdentifier()) || !mechanic.HasEquippedItem("weldingtool".ToIdentifier())); // Wait until equipped
SetDoorAccess(mechanic_secondDoor, mechanic_secondDoorLight, true);
mechanic.AddActiveObjectiveEntity(mechanic_brokenWall_1, mechanic_weldIcon, mechanic_repairIconColor);
do { yield return null; } while (WallHasDamagedSections(mechanic_brokenWall_1)); // Highlight until repaired
mechanic.RemoveActiveObjectiveEntity(mechanic_brokenWall_1);
RemoveCompletedObjective(segments[2]);
RemoveCompletedObjective(2);
yield return new WaitForSeconds(1f, false);
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Select)); // Pump objective
TriggerTutorialSegment(3, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select)); // Pump objective
SetHighlight(mechanic_workingPump.Item, true);
do
{
@@ -333,9 +407,9 @@ namespace Barotrauma.Tutorials
} while (mechanic_workingPump.FlowPercentage >= 0 || !mechanic_workingPump.IsActive); // Highlight until draining
SetHighlight(mechanic_workingPump.Item, false);
do { yield return null; } while (mechanic_brokenhull_1.WaterPercentage > waterVolumeBeforeOpening); // Unlock door once drained
RemoveCompletedObjective(segments[3]);
RemoveCompletedObjective(3);
SetDoorAccess(mechanic_thirdDoor, mechanic_thirdDoorLight, true);
//TriggerTutorialSegment(11, GameMain.Config.KeyBind(InputType.Select), GameMain.Config.KeyBind(InputType.Up), GameMain.Config.KeyBind(InputType.Down), GameMain.Config.KeyBind(InputType.Select)); // Ladder objective
//TriggerTutorialSegment(11, GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select], GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Up], GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Down], GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select]); // Ladder objective
//do { yield return null; } while (!mechanic_ladderSensor.MotionDetected);
//RemoveCompletedObjective(segments[11]);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.News"), ChatMessageType.Radio, null);
@@ -362,24 +436,24 @@ namespace Barotrauma.Tutorials
if (mechanic.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); }
}
if (mechanic.Inventory.FindItemByIdentifier("oxygentank") == null && mechanic.Inventory.FindItemByIdentifier("aluminium") == null)
if (mechanic.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) == null && mechanic.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) == null)
{
for (int i = 0; i < mechanic_craftingCabinet.Capacity; i++)
{
Item item = mechanic_craftingCabinet.Inventory.GetItemAt(i);
if (item != null && item.prefab.Identifier == "oxygentank")
if (item != null && item.Prefab.Identifier == "oxygentank")
{
HighlightInventorySlot(mechanic_craftingCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
}
}
}
if (mechanic.Inventory.FindItemByIdentifier("sodium") == null)
if (mechanic.Inventory.FindItemByIdentifier("sodium".ToIdentifier()) == null)
{
for (int i = 0; i < mechanic_craftingCabinet.Inventory.Capacity; i++)
{
Item item = mechanic_craftingCabinet.Inventory.GetItemAt(i);
if (item != null && item.prefab.Identifier == "sodium")
if (item != null && item.Prefab.Identifier == "sodium")
{
HighlightInventorySlot(mechanic_craftingCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
}
@@ -387,12 +461,12 @@ namespace Barotrauma.Tutorials
}
}
if (!gotOxygenTank && (mechanic.Inventory.FindItemByIdentifier("oxygentank") != null ||
mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") != null))
if (!gotOxygenTank && (mechanic.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) != null ||
mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) != null))
{
gotOxygenTank = true;
}
if (!gotSodium && mechanic.Inventory.FindItemByIdentifier("sodium") != null)
if (!gotSodium && mechanic.Inventory.FindItemByIdentifier("sodium".ToIdentifier()) != null)
{
gotSodium = true;
}
@@ -406,9 +480,9 @@ namespace Barotrauma.Tutorials
{
if (IsSelectedItem(mechanic_deconstructor.Item))
{
if (mechanic_deconstructor.OutputContainer.Inventory.FindItemByIdentifier("aluminium") != null)
if (mechanic_deconstructor.OutputContainer.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) != null)
{
HighlightInventorySlot(mechanic_deconstructor.OutputContainer.Inventory, "aluminium", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic_deconstructor.OutputContainer.Inventory, "aluminium".ToIdentifier(), highlightColor, .5f, .5f, 0f);
for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{
@@ -417,16 +491,16 @@ namespace Barotrauma.Tutorials
}
else
{
if (mechanic.Inventory.FindItemByIdentifier("oxygentank") != null && mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") == null)
if (mechanic.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) != null && mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) == null)
{
HighlightInventorySlot(mechanic.Inventory, "oxygentank", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "oxygentank".ToIdentifier(), highlightColor, .5f, .5f, 0f);
for (int i = 0; i < mechanic_deconstructor.InputContainer.Inventory.Capacity; i++)
{
HighlightInventorySlot(mechanic_deconstructor.InputContainer.Inventory, i, highlightColor, .5f, .5f, 0f);
}
}
if (mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") != null && !mechanic_deconstructor.IsActive)
if (mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) != null && !mechanic_deconstructor.IsActive)
{
if (mechanic_deconstructor.ActivateButton.FlashTimer <= 0)
{
@@ -437,11 +511,11 @@ namespace Barotrauma.Tutorials
}
yield return null;
} while (
mechanic.Inventory.FindItemByIdentifier("aluminium") == null &&
mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("aluminium") == null); // Wait until aluminium obtained
mechanic.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) == null &&
mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) == null); // Wait until aluminium obtained
SetHighlight(mechanic_deconstructor.Item, false);
RemoveCompletedObjective(segments[4]);
SetHighlight(mechanic_deconstructor.Item, false);
RemoveCompletedObjective(4);
yield return new WaitForSeconds(1f, false);
TriggerTutorialSegment(5); // Fabricate
SetHighlight(mechanic_fabricator.Item, true);
@@ -455,26 +529,26 @@ namespace Barotrauma.Tutorials
}
else
{
if (mechanic_fabricator.OutputContainer.Inventory.FindItemByIdentifier("extinguisher") != null)
if (mechanic_fabricator.OutputContainer.Inventory.FindItemByIdentifier("extinguisher".ToIdentifier()) != null)
{
HighlightInventorySlot(mechanic_fabricator.OutputContainer.Inventory, "extinguisher", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic_fabricator.OutputContainer.Inventory, "extinguisher".ToIdentifier(), highlightColor, .5f, .5f, 0f);
/*for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{
if (mechanic.Inventory.Items[i] == null) HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f);
}*/
}
else if (mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("aluminium") != null && mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("sodium") != null && !mechanic_fabricator.IsActive)
else if (mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) != null && mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("sodium".ToIdentifier()) != null && !mechanic_fabricator.IsActive)
{
if (mechanic_fabricator.ActivateButton.FlashTimer <= 0)
{
mechanic_fabricator.ActivateButton.Flash(highlightColor, 1.5f, false);
}
}
else if (mechanic.Inventory.FindItemByIdentifier("aluminium") != null || mechanic.Inventory.FindItemByIdentifier("sodium") != null)
else if (mechanic.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) != null || mechanic.Inventory.FindItemByIdentifier("sodium".ToIdentifier()) != null)
{
HighlightInventorySlot(mechanic.Inventory, "aluminium", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "sodium", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "aluminium".ToIdentifier(), highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "sodium".ToIdentifier(), highlightColor, .5f, .5f, 0f);
if (mechanic_fabricator.InputContainer.Inventory.GetItemAt(0) == null)
{
@@ -489,27 +563,27 @@ namespace Barotrauma.Tutorials
}
}
yield return null;
} while (mechanic.Inventory.FindItemByIdentifier("extinguisher") == null); // Wait until extinguisher is created
RemoveCompletedObjective(segments[5]);
} while (mechanic.Inventory.FindItemByIdentifier("extinguisher".ToIdentifier()) == null); // Wait until extinguisher is created
RemoveCompletedObjective(5);
SetHighlight(mechanic_fabricator.Item, false);
SetDoorAccess(mechanic_fourthDoor, mechanic_fourthDoorLight, true);
// Room 5
do { yield return null; } while (!mechanic_fireSensor.MotionDetected);
TriggerTutorialSegment(6, GameMain.Config.KeyBindText(InputType.Aim), GameMain.Config.KeyBindText(InputType.Shoot)); // Using the extinguisher
TriggerTutorialSegment(6, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot)); // Using the extinguisher
do { yield return null; } while (!mechanic_fire.Removed); // Wait until extinguished
yield return new WaitForSeconds(3f, false);
RemoveCompletedObjective(segments[6]);
RemoveCompletedObjective(6);
if (mechanic.HasEquippedItem("extinguisher")) // do not trigger if dropped already
if (mechanic.HasEquippedItem("extinguisher".ToIdentifier())) // do not trigger if dropped already
{
TriggerTutorialSegment(7);
do
{
HighlightInventorySlot(mechanic.Inventory, "extinguisher", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "extinguisher".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
yield return null;
} while (mechanic.HasEquippedItem("extinguisher"));
RemoveCompletedObjective(segments[7]);
} while (mechanic.HasEquippedItem("extinguisher".ToIdentifier()));
RemoveCompletedObjective(7);
}
SetDoorAccess(mechanic_fifthDoor, mechanic_fifthDoorLight, true);
@@ -531,9 +605,9 @@ namespace Barotrauma.Tutorials
}
}
yield return null;
} while (!mechanic.HasEquippedItem("divingsuit", slotType: InvSlotType.OuterClothes));
} while (!mechanic.HasEquippedItem("divingsuit".ToIdentifier(), slotType: InvSlotType.OuterClothes));
SetHighlight(mechanic_divingSuitContainer.Item, false);
RemoveCompletedObjective(segments[8]);
RemoveCompletedObjective(8);
SetDoorAccess(tutorial_mechanicFinalDoor, tutorial_mechanicFinalDoorLight, true);
// Room 7
@@ -542,7 +616,7 @@ namespace Barotrauma.Tutorials
mechanic.RemoveActiveObjectiveEntity(mechanic_brokenWall_2);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(9, GameMain.Config.KeyBindText(InputType.Use)); // Repairing machinery (pump)
TriggerTutorialSegment(9, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Use)); // Repairing machinery (pump)
SetHighlight(mechanic_brokenPump.Item, true);
mechanic_brokenPump.CanBeSelected = true;
Repairable repairablePumpComponent = mechanic_brokenPump.Item.GetComponent<Repairable>();
@@ -552,9 +626,9 @@ namespace Barotrauma.Tutorials
yield return null;
if (repairablePumpComponent.IsBelowRepairThreshold)
{
if (!mechanic.HasEquippedItem("wrench"))
if (!mechanic.HasEquippedItem("wrench".ToIdentifier()))
{
HighlightInventorySlot(mechanic.Inventory, "wrench", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "wrench".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
}
else if (IsSelectedItem(mechanic_brokenPump.Item) && repairablePumpComponent.CurrentFixer == null)
{
@@ -575,7 +649,7 @@ namespace Barotrauma.Tutorials
}
}
} while (repairablePumpComponent.IsBelowRepairThreshold || mechanic_brokenPump.FlowPercentage >= 0 || !mechanic_brokenPump.IsActive);
RemoveCompletedObjective(segments[9]);
RemoveCompletedObjective(9);
SetHighlight(mechanic_brokenPump.Item, false);
do { yield return null; } while (mechanic_brokenhull_2.WaterPercentage > waterVolumeBeforeOpening);
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
@@ -599,7 +673,7 @@ namespace Barotrauma.Tutorials
// Remove highlights when each individual machine is repaired
do { CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent); yield return null; } while (repairablePumpComponent1.IsBelowRepairThreshold || repairablePumpComponent2.IsBelowRepairThreshold || repairableEngineComponent.IsBelowRepairThreshold);
CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent);
RemoveCompletedObjective(segments[10]);
RemoveCompletedObjective(10);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Complete"), ChatMessageType.Radio, null);
// END TUTORIAL

View File

@@ -72,62 +72,114 @@ namespace Barotrauma.Tutorials
private PowerContainer officer_subSuperCapacitor_2;
// Variables
private string radioSpeakerName;
private LocalizedString radioSpeakerName;
private Character officer;
private float superCapacitorRechargeRate = 10;
private Sprite officer_gunIcon;
private Color officer_gunIconColor;
public OfficerTutorial(XElement element) : base(element)
public OfficerTutorial() : base("tutorial.securityofficertraining".ToIdentifier(),
new Segment(
"Mechanic.Equipment".ToIdentifier(),
"Mechanic.EquipmentObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Mechanic.EquipmentText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Officer.MeleeWeapon".ToIdentifier(),
"Officer.MeleeWeaponObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Officer.MeleeWeaponText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Officer.Crawler".ToIdentifier(),
"Officer.CrawlerObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Officer.CrawlerText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Officer.SomethingBig".ToIdentifier(),
"Officer.SomethingBigObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Officer.SomethingBigText".ToIdentifier(), Width = 700, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_loaders.webm", TextTag = "Officer.SomethingBigText".ToIdentifier(), Width = 700, Height = 80 }),
new Segment(
"Officer.Hammerhead".ToIdentifier(),
"Officer.HammerheadObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Officer.HammerheadText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Officer.RangedWeapon".ToIdentifier(),
"Officer.RangedWeaponObjective".ToIdentifier(),
TutorialContentType.ManualVideo,
textContent: new Segment.Text { Tag = "Officer.RangedWeaponText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center },
videoContent: new Segment.Video { File = "tutorial_ranged.webm", TextTag = "Officer.RangedWeaponText".ToIdentifier(), Width = 450, Height = 80 }),
new Segment(
"Officer.Mudraptor".ToIdentifier(),
"Officer.MudraptorObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Officer.MudraptorText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }),
new Segment(
"Officer.ArmSubmarine".ToIdentifier(),
"Officer.ArmSubmarineObjective".ToIdentifier(),
TutorialContentType.TextOnly,
textContent: new Segment.Text { Tag = "Officer.ArmSubmarineText".ToIdentifier(), Width = 450, Height = 80, Anchor = Anchor.Center }))
{ }
protected override CharacterInfo GetCharacterInfo()
{
return new CharacterInfo(
CharacterPrefab.HumanSpeciesName,
jobOrJobPrefab: new Job(
JobPrefab.Prefabs["securityofficer"], Rand.RandSync.Unsynced, 0,
new Skill("medical".ToIdentifier(), 20),
new Skill("weapons".ToIdentifier(), 70),
new Skill("mechanical".ToIdentifier(), 20),
new Skill("electrical".ToIdentifier(), 20),
new Skill("helm".ToIdentifier(), 20)));
}
public override void Start()
protected override void Initialize()
{
base.Start();
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
officer = Character.Controlled;
var handcuffs = FindOrGiveItem(officer, "handcuffs");
var handcuffs = FindOrGiveItem(officer, "handcuffs".ToIdentifier());
handcuffs.Unequip(officer);
officer.Inventory.RemoveItem(handcuffs);
var stunbaton = FindOrGiveItem(officer, "stunbaton");
var stunbaton = FindOrGiveItem(officer, "stunbaton".ToIdentifier());
stunbaton.Unequip(officer);
officer.Inventory.RemoveItem(stunbaton);
var smg = FindOrGiveItem(officer, "smg");
var smg = FindOrGiveItem(officer, "smg".ToIdentifier());
smg.Unequip(officer);
officer.Inventory.RemoveItem(smg);
var divingknife = FindOrGiveItem(officer, "divingknife");
var divingknife = FindOrGiveItem(officer, "divingknife".ToIdentifier());
divingknife.Unequip(officer);
officer.Inventory.RemoveItem(divingknife);
var steroids = FindOrGiveItem(officer, "steroids");
var steroids = FindOrGiveItem(officer, "steroids".ToIdentifier());
steroids.Unequip(officer);
officer.Inventory.RemoveItem(steroids);
var ballistichelmet =
officer.Inventory.FindItemByIdentifier("ballistichelmet1") ??
officer.Inventory.FindItemByIdentifier("ballistichelmet2") ??
FindOrGiveItem(officer, "ballistichelmet3");
officer.Inventory.FindItemByIdentifier("ballistichelmet1".ToIdentifier()) ??
officer.Inventory.FindItemByIdentifier("ballistichelmet2".ToIdentifier()) ??
FindOrGiveItem(officer, "ballistichelmet3".ToIdentifier());
ballistichelmet.Unequip(officer);
officer.Inventory.RemoveItem(ballistichelmet);
var bodyarmor = FindOrGiveItem(officer, "bodyarmor");
var bodyarmor = FindOrGiveItem(officer, "bodyarmor".ToIdentifier());
bodyarmor.Unequip(officer);
officer.Inventory.RemoveItem(bodyarmor);
var gunOrder = Order.GetPrefab("operateweapons");
var gunOrder = OrderPrefab.Prefabs["operateweapons"];
officer_gunIcon = gunOrder.SymbolSprite;
officer_gunIconColor = gunOrder.Color;
var bandage = FindOrGiveItem(officer, "antibleeding1");
var bandage = FindOrGiveItem(officer, "antibleeding1".ToIdentifier());
bandage.Unequip(officer);
officer.Inventory.RemoveItem(bandage);
FindOrGiveItem(officer, "antibleeding1");
FindOrGiveItem(officer, "antibleeding1".ToIdentifier());
// Other tutorial items
tutorial_mechanicFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoorlight")).GetComponent<LightComponent>();
@@ -222,7 +274,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!officer_equipmentObjectiveSensor.MotionDetected);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.Equipment"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(3f, false);
//TriggerTutorialSegment(0, GameMain.Config.KeyBind(InputType.Select), GameMain.Config.KeyBind(InputType.Deselect)); // Retrieve equipment
//TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select], GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Deselect]); // Retrieve equipment
SetHighlight(officer_equipmentCabinet.Item, true);
bool firstSlotRemoved = false;
bool secondSlotRemoved = false;
@@ -260,24 +312,24 @@ namespace Barotrauma.Tutorials
//RemoveCompletedObjective(segments[0]);
SetHighlight(officer_equipmentCabinet.Item, false);
do { yield return null; } while (IsSelectedItem(officer_equipmentCabinet.Item));
TriggerTutorialSegment(1, GameMain.Config.KeyBindText(InputType.Aim), GameMain.Config.KeyBindText(InputType.Shoot)); // Equip melee weapon & armor
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot)); // Equip melee weapon & armor
do
{
if (!officer.HasEquippedItem("stunbaton"))
if (!officer.HasEquippedItem("stunbaton".ToIdentifier()))
{
HighlightInventorySlot(officer.Inventory, "stunbaton", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(officer.Inventory, "stunbaton".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
if (!officer.HasEquippedItem("bodyarmor"))
if (!officer.HasEquippedItem("bodyarmor".ToIdentifier()))
{
HighlightInventorySlot(officer.Inventory, "bodyarmor", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(officer.Inventory, "bodyarmor".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
if (!officer.HasEquippedItem("ballistichelmet1"))
if (!officer.HasEquippedItem("ballistichelmet1".ToIdentifier()))
{
HighlightInventorySlot(officer.Inventory, "ballistichelmet1", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(officer.Inventory, "ballistichelmet1".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return new WaitForSeconds(1f, false);
} while (!officer.HasEquippedItem("stunbaton") || !officer.HasEquippedItem("bodyarmor") || !officer.HasEquippedItem("ballistichelmet1"));
RemoveCompletedObjective(segments[1]);
} while (!officer.HasEquippedItem("stunbaton".ToIdentifier()) || !officer.HasEquippedItem("bodyarmor".ToIdentifier()) || !officer.HasEquippedItem("ballistichelmet1".ToIdentifier()));
RemoveCompletedObjective(1);
SetDoorAccess(officer_firstDoor, officer_firstDoorLight, true);
// Room 3
@@ -285,7 +337,7 @@ namespace Barotrauma.Tutorials
TriggerTutorialSegment(2);
officer_crawler = SpawnMonster("crawler", officer_crawlerSpawnPos);
do { yield return null; } while (!officer_crawler.IsDead);
RemoveCompletedObjective(segments[2]);
RemoveCompletedObjective(2);
Heal(officer);
yield return new WaitForSeconds(1f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.CrawlerDead"), ChatMessageType.Radio, null);
@@ -305,7 +357,7 @@ namespace Barotrauma.Tutorials
SetHighlight(officer_ammoShelf_2.Item, officer_coilgunLoader.Item.ExternalHighlight );
if (IsSelectedItem(officer_coilgunLoader.Item))
{
HighlightInventorySlot(officer.Inventory, "coilgunammobox", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(officer.Inventory, "coilgunammobox".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return null;
} while (officer_coilgunLoader.Inventory.GetItemAt(0) == null || officer_superCapacitor.RechargeSpeed < superCapacitorRechargeRate || officer_coilgunLoader.Inventory.GetItemAt(0).Condition == 0);
@@ -313,9 +365,9 @@ namespace Barotrauma.Tutorials
SetHighlight(officer_superCapacitor.Item, false);
SetHighlight(officer_ammoShelf_1.Item, false);
SetHighlight(officer_ammoShelf_2.Item, false);
RemoveCompletedObjective(segments[3]);
RemoveCompletedObjective(3);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect)); // Kill hammerhead
TriggerTutorialSegment(4, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Kill hammerhead
officer_hammerhead = SpawnMonster("hammerhead", officer_hammerheadSpawnPos);
officer_hammerhead.Params.AI.AvoidAbyss = false;
officer_hammerhead.Params.AI.StayInAbyss = false;
@@ -348,7 +400,7 @@ namespace Barotrauma.Tutorials
while(!officer_hammerhead.IsDead);
Heal(officer);
SetHighlight(officer_coilgunPeriscope, false);
RemoveCompletedObjective(segments[4]);
RemoveCompletedObjective(4);
yield return new WaitForSeconds(1f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.HammerheadDead"), ChatMessageType.Radio, null);
SetDoorAccess(officer_thirdDoor, officer_thirdDoorLight, true);
@@ -357,16 +409,16 @@ namespace Barotrauma.Tutorials
//do { yield return null; } while (!officer_rangedWeaponSensor.MotionDetected);
do { yield return null; } while (!officer_thirdDoor.IsOpen);
yield return new WaitForSeconds(3f, false);
TriggerTutorialSegment(5, GameMain.Config.KeyBindText(InputType.Aim), GameMain.Config.KeyBindText(InputType.Shoot)); // Ranged weapons
TriggerTutorialSegment(5, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot)); // Ranged weapons
SetHighlight(officer_rangedWeaponHolder.Item, true);
do { yield return null; } while (!officer_rangedWeaponHolder.Inventory.IsEmpty()); // Wait until looted
SetHighlight(officer_rangedWeaponHolder.Item, false);
do
{
HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(officer.Inventory, "shotgun".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
yield return null;
} while (!officer.HasEquippedItem("shotgun")); // Wait until equipped
ItemContainer shotGunChamber = officer.Inventory.FindItemByIdentifier("shotgun").GetComponent<ItemContainer>();
} while (!officer.HasEquippedItem("shotgun".ToIdentifier())); // Wait until equipped
ItemContainer shotGunChamber = officer.Inventory.FindItemByIdentifier("shotgun".ToIdentifier()).GetComponent<ItemContainer>();
SetHighlight(officer_rangedWeaponCabinet.Item, true);
do
{
@@ -392,13 +444,13 @@ namespace Barotrauma.Tutorials
}
}
if (officer.Inventory.FindItemByIdentifier("shotgunshell") != null || (IsSelectedItem(officer_rangedWeaponCabinet.Item) && officer_rangedWeaponCabinet.Inventory.FindItemByIdentifier("shotgunshell") != null))
if (officer.Inventory.FindItemByIdentifier("shotgunshell".ToIdentifier()) != null || (IsSelectedItem(officer_rangedWeaponCabinet.Item) && officer_rangedWeaponCabinet.Inventory.FindItemByIdentifier("shotgunshell".ToIdentifier()) != null))
{
HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(officer.Inventory, "shotgun".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
}
yield return null;
} while (!shotGunChamber.Inventory.IsFull(takeStacksIntoAccount: true)); // Wait until all six harpoons loaded
RemoveCompletedObjective(segments[5]);
RemoveCompletedObjective(5);
SetHighlight(officer_rangedWeaponCabinet.Item, false);
SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true);
@@ -408,7 +460,7 @@ namespace Barotrauma.Tutorials
officer_mudraptor = SpawnMonster("mudraptor", officer_mudraptorSpawnPos);
do { yield return null; } while (!officer_mudraptor.IsDead);
Heal(officer);
RemoveCompletedObjective(segments[6]);
RemoveCompletedObjective(6);
SetDoorAccess(tutorial_securityFinalDoor, tutorial_securityFinalDoorLight, true);
// Submarine
@@ -459,7 +511,7 @@ namespace Barotrauma.Tutorials
officer.RemoveActiveObjectiveEntity(officer_subSuperCapacitor_2.Item);
officer.RemoveActiveObjectiveEntity(officer_subAmmoBox_1);
officer.RemoveActiveObjectiveEntity(officer_subAmmoBox_2);
RemoveCompletedObjective(segments[7]);
RemoveCompletedObjective(7);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.Complete"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(4f, false);

View File

@@ -8,18 +8,21 @@ using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
class ScenarioTutorial : Tutorial
abstract class ScenarioTutorial : Tutorial
{
private CoroutineHandle tutorialCoroutine;
private Character character;
private string spawnSub;
private SpawnType spawnPointType;
private string submarinePath;
private string startOutpostPath;
private string endOutpostPath;
private string levelSeed;
private string levelParams;
private const string submarinePath = "Content/Tutorials/Dugong_Tutorial.sub";
private const string startOutpostPath = "Content/Tutorials/TutorialOutpost.sub";
//private const string endOutpostPath = "";
private const string levelSeed = "nLoZLLtza";
private const string levelParams = "ColdCavernsTutorial";
//private const string spawnSub = "startoutpost";
private const SpawnType spawnPointType = SpawnType.Human;
private SubmarineInfo startOutpost = null;
private SubmarineInfo endOutpost = null;
@@ -31,34 +34,18 @@ namespace Barotrauma.Tutorials
protected Color highlightColor = Color.OrangeRed;
protected Color uiHighlightColor = new Color(150, 50, 0);
protected Color buttonHighlightColor = new Color(255, 100, 0);
protected Color inaccessibleColor = GUI.Style.Red;
protected Color accessibleColor = GUI.Style.Green;
protected Color inaccessibleColor = GUIStyle.Red;
protected Color accessibleColor = GUIStyle.Green;
public ScenarioTutorial(XElement element) : base(element)
{
submarinePath = element.GetAttributeString("submarinepath", "");
startOutpostPath = element.GetAttributeString("startoutpostpath", "");
endOutpostPath = element.GetAttributeString("endoutpostpath", "");
protected ScenarioTutorial(Identifier identifier, params Segment[] segments) : base(identifier, segments) { }
levelSeed = element.GetAttributeString("levelseed", "tuto");
levelParams = element.GetAttributeString("levelparams", "");
spawnSub = element.GetAttributeString("spawnsub", "");
Enum.TryParse(element.GetAttributeString("spawnpointtype", "Human"), true, out spawnPointType);
}
public override void Initialize()
{
base.Initialize();
currentTutorialCompleted = false;
GameMain.Instance.ShowLoading(Loading());
}
private IEnumerable<CoroutineStatus> Loading()
protected abstract void Initialize();
protected override IEnumerable<CoroutineStatus> Loading()
{
SubmarineInfo subInfo = new SubmarineInfo(submarinePath);
LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Identifier.Equals(levelParams, StringComparison.OrdinalIgnoreCase));
LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Identifier == levelParams);
yield return CoroutineStatus.Running;
@@ -68,18 +55,18 @@ namespace Barotrauma.Tutorials
if (generationParams != null)
{
Biome biome =
LevelGenerationParams.GetBiomes().FirstOrDefault(b => generationParams.AllowedBiomes.Contains(b)) ??
LevelGenerationParams.GetBiomes().First();
Biome.Prefabs.FirstOrDefault(b => generationParams.AllowedBiomeIdentifiers.Contains(b.Identifier)) ??
Biome.Prefabs.First();
if (!string.IsNullOrEmpty(startOutpostPath))
{
startOutpost = new SubmarineInfo(startOutpostPath);
}
if (!string.IsNullOrEmpty(endOutpostPath))
/*if (!string.IsNullOrEmpty(endOutpostPath))
{
endOutpost = new SubmarineInfo(endOutpostPath);
}
}*/
LevelData tutorialLevel = new LevelData(levelSeed, 0, 0, generationParams, biome);
GameMain.GameSession.StartRound(tutorialLevel, startOutpost: startOutpost, endOutpost: endOutpost);
@@ -93,12 +80,6 @@ namespace Barotrauma.Tutorials
GameMain.GameSession.EventManager.Enabled = false;
GameMain.GameScreen.Select();
yield return CoroutineStatus.Success;
}
public override void Start()
{
base.Start();
Submarine.MainSub.GodMode = true;
foreach (Structure wall in Structure.WallList)
@@ -109,16 +90,15 @@ namespace Barotrauma.Tutorials
}
}
CharacterInfo charInfo = configElement.Element("Character") == null ?
new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer")) :
new CharacterInfo(configElement.Element("Character"));
CharacterInfo charInfo = GetCharacterInfo();
WayPoint wayPoint = GetSpawnPoint(charInfo);
if (wayPoint == null)
{
DebugConsole.ThrowError("A waypoint with the spawntype \"" + spawnPointType + "\" is required for the tutorial event");
return;
yield return CoroutineStatus.Failure;
yield break;
}
character = Character.Create(charInfo, wayPoint.WorldPosition, "", isRemotePlayer: false, hasAi: false);
@@ -126,11 +106,12 @@ namespace Barotrauma.Tutorials
Character.Controlled = character;
character.GiveJobItems(null);
var idCard = character.Inventory.FindItemByIdentifier("idcard");
var idCard = character.Inventory.FindItemByIdentifier("idcard".ToIdentifier());
if (idCard == null)
{
DebugConsole.ThrowError("Item prefab \"ID Card\" not found!");
return;
yield return CoroutineStatus.Failure;
yield break;
}
idCard.AddTag("com");
idCard.AddTag("eng");
@@ -145,8 +126,14 @@ namespace Barotrauma.Tutorials
}
tutorialCoroutine = CoroutineManager.StartCoroutine(UpdateState());
Initialize();
yield return CoroutineStatus.Success;
}
protected abstract CharacterInfo GetCharacterInfo();
public override void AddToGUIUpdateList()
{
if (!currentTutorialCompleted)
@@ -157,7 +144,7 @@ namespace Barotrauma.Tutorials
private WayPoint GetSpawnPoint(CharacterInfo charInfo)
{
Submarine spawnSub = null;
/*Submarine spawnSub = null;
if (this.spawnSub != string.Empty)
{
@@ -175,15 +162,15 @@ namespace Barotrauma.Tutorials
spawnSub = Submarine.MainSub;
break;
}
}
}*/
Submarine spawnSub = Level.Loaded.StartOutpost;
return WayPoint.GetRandom(spawnPointType, charInfo.Job?.Prefab, spawnSub);
}
protected bool HasOrder(Character character, string identifier, string option = null)
{
var currentOrderInfo = character.GetCurrentOrderWithTopPriority();
if (currentOrderInfo?.Order?.Identifier == identifier)
if (currentOrderInfo?.Identifier == identifier)
{
if (option == null)
{
@@ -191,7 +178,7 @@ namespace Barotrauma.Tutorials
}
else
{
return currentOrderInfo?.OrderOption == option;
return currentOrderInfo?.Option == option;
}
}
@@ -267,7 +254,7 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(3.0f);
var messageBox = new GUIMessageBox(TextManager.Get("Tutorial.TryAgainHeader"), TextManager.Get("Tutorial.TryAgain"), new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
var messageBox = new GUIMessageBox(TextManager.Get("Tutorial.TryAgainHeader"), TextManager.Get("Tutorial.TryAgain"), new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
messageBox.Buttons[0].OnClicked += Restart;
messageBox.Buttons[0].OnClicked += messageBox.Close;
@@ -303,7 +290,7 @@ namespace Barotrauma.Tutorials
character.SetStun(0.0f, true);
}
protected Item FindOrGiveItem(Character character, string identifier)
protected Item FindOrGiveItem(Character character, Identifier identifier)
{
var item = character.Inventory.FindItemByIdentifier(identifier);
if (item != null && !item.Removed) { return item; }

View File

@@ -7,189 +7,128 @@ using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
using System.Collections.Immutable;
namespace Barotrauma.Tutorials
{
enum TutorialContentType { None = 0, Video = 1, ManualVideo = 2, TextOnly = 3 };
/// <summary>
/// If you're seeing this and are currently working on improving the tutorials, consider
/// deleting this class and all that derive from it, and starting from scratch.
/// </summary>
abstract class Tutorial
{
#region Tutorial variables
public static bool Initialized = false;
public static bool ContentRunning = false;
public static List<Tutorial> Tutorials;
#region Constants
public const string PlayableContentPath = "Content/Tutorials/TutorialVideos/";
#endregion
#region Tutorial variables
public static ImmutableHashSet<Type> Types;
static Tutorial()
{
Types = ReflectionUtils.GetDerivedNonAbstract<Tutorial>()
.ToImmutableHashSet();
}
public readonly Identifier Identifier;
public LocalizedString DisplayName { get; }
public bool ContentRunning { get; protected set; }
protected bool started = false;
protected GUIComponent infoBox;
private Action infoBoxClosedCallback;
protected XElement configElement;
protected VideoPlayer videoPlayer;
protected enum TutorialContentTypes { None = 0, Video = 1, ManualVideo = 2, TextOnly = 3 };
protected string playableContentPath;
protected Point screenResolution;
protected WindowMode windowMode;
protected float prevUIScale;
private GUIFrame holderFrame, objectiveFrame;
private List<TutorialSegment> activeObjectives = new List<TutorialSegment>();
private string objectiveTranslated;
private readonly List<Index> activeObjectives;
private readonly LocalizedString objectiveTranslated;
protected TutorialSegment activeContentSegment;
protected List<TutorialSegment> segments;
protected readonly ImmutableArray<Segment> segments;
protected Index activeContentSegmentIndex;
protected Segment activeContentSegment => segments[activeContentSegmentIndex];
protected class TutorialSegment
protected class Segment
{
public string Id;
public string Objective;
public TutorialContentTypes ContentType;
public XElement TextContent;
public XElement VideoContent;
public struct Text
{
public Identifier Tag;
public int Width;
public int Height;
public Anchor Anchor;
}
public struct Video
{
public string File;
public Identifier TextTag;
public int Width;
public int Height;
}
public bool IsTriggered;
public GUIButton ReplayButton;
public GUITextBlock LinkedTitle, LinkedText;
public object[] Args;
public LocalizedString Objective;
public TutorialSegment(XElement config)
public readonly Identifier Id;
public readonly Text? TextContent;
public readonly Video? VideoContent;
public readonly TutorialContentType ContentType;
public Segment(Identifier id, Identifier objectiveTextTag, TutorialContentType contentType, Text? textContent = null, Video? videoContent = null)
{
Id = config.GetAttributeString("id", "Missing ID");
Objective = TextManager.Get(config.GetAttributeString("objective", string.Empty), true);
Enum.TryParse(config.GetAttributeString("contenttype", "None"), true, out ContentType);
IsTriggered = config.GetAttributeBool("istriggered", false);
Id = id;
Objective = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
ContentType = contentType;
TextContent = textContent;
VideoContent = videoContent;
switch (ContentType)
{
case TutorialContentTypes.None:
break;
case TutorialContentTypes.Video:
case TutorialContentTypes.ManualVideo:
VideoContent = config.Element("Video");
TextContent = config.Element("Text");
break;
case TutorialContentTypes.TextOnly:
TextContent = config.Element("Text");
break;
}
IsTriggered = false;
}
}
public string Identifier
{
get;
protected set;
}
public string DisplayName
{
get;
protected set;
}
private bool completed;
public bool Completed
{
get { return completed; }
protected set
{
if (completed == value) return;
if (completed == value) { return; }
completed = value;
GameMain.Config.SaveNewPlayerConfig();
if (value)
{
CompletedTutorials.Instance.Add(Identifier);
}
GameSettings.SaveCurrentConfig();
}
}
#endregion
#region Tutorial Controls
public static void Init()
protected Tutorial(Identifier identifier, params Segment[] segments)
{
Tutorials = new List<Tutorial>();
foreach (ContentFile file in GameMain.Instance.GetFilesOfType(ContentType.Tutorials))
{
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
if (doc?.Root == null) continue;
foreach (XElement element in doc.Root.Elements())
{
Tutorial newTutorial = Load(element);
if (newTutorial != null) Tutorials.Add(newTutorial);
}
}
}
private static Tutorial Load(XElement element)
{
Type t;
string type = element.Name.ToString().ToLowerInvariant();
try
{
// Get the type of a specified class.
t = Type.GetType("Barotrauma.Tutorials." + type + "", false, true);
if (t == null)
{
DebugConsole.ThrowError("Could not find tutorial type \"" + type + "\"");
return null;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Could not find tutorial type \"" + type + "\"", e);
return null;
}
ConstructorInfo constructor;
try
{
if (!t.IsSubclassOf(typeof(Tutorial))) return null;
constructor = t.GetConstructor(new Type[] { typeof(XElement) });
if (constructor == null)
{
DebugConsole.ThrowError("Could not find the constructor of tutorial type \"" + type + "\"");
return null;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Could not find the constructor of tutorial type \"" + type + "\"", e);
return null;
}
Tutorial tutorial = null;
try
{
object component = constructor.Invoke(new object[] { element });
tutorial = (Tutorial)component;
}
catch (TargetInvocationException e)
{
DebugConsole.ThrowError("Error while loading tutorial of the type " + t + ".", e.InnerException);
}
return tutorial;
}
public Tutorial(XElement element)
{
configElement = element;
Identifier = element.GetAttributeString("identifier", "unknown");
Identifier = identifier;
this.segments = segments.ToImmutableArray();
DisplayName = TextManager.Get(Identifier);
completed = GameMain.Config.CompletedTutorialNames.Contains(Identifier);
playableContentPath = element.GetAttributeString("playablecontentpath", "");
segments = new List<TutorialSegment>();
foreach (var segment in element.Elements("Segment"))
{
segments.Add(new TutorialSegment(segment));
}
}
public virtual void Initialize()
{
if (Initialized) return;
Initialized = true;
videoPlayer = new VideoPlayer();
}
public virtual void Start()
{
activeObjectives.Clear();
activeObjectives = new List<Index>();
objectiveTranslated = TextManager.Get("Tutorial.Objective");
}
protected abstract IEnumerable<CoroutineStatus> Loading();
public void Start()
{
videoPlayer = new VideoPlayer();
GameMain.Instance.ShowLoading(Loading());
activeObjectives.Clear();
CreateObjectiveFrame();
// Setup doors: Clear all requirements, unless the door is setup as locked.
@@ -208,7 +147,7 @@ namespace Barotrauma.Tutorials
public virtual void AddToGUIUpdateList()
{
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale || GameMain.Config.WindowMode != windowMode)
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale || GameSettings.CurrentConfig.Graphics.DisplayMode != windowMode)
{
CreateObjectiveFrame();
}
@@ -255,74 +194,69 @@ namespace Barotrauma.Tutorials
protected bool Restart(GUIButton button, object obj)
{
GUI.PreventPauseMenuToggle = false;
TutorialMode.StartTutorial(this);
return true;
}
protected virtual void TriggerTutorialSegment(int index, params object[] args)
protected virtual void TriggerTutorialSegment(Index index, params object[] args)
{
Inventory.DraggingItems.Clear();
ContentRunning = true;
activeContentSegment = segments[index];
activeContentSegmentIndex = index;
segments[index].Args = args;
string tutorialText = TextManager.GetFormatted(activeContentSegment.TextContent.GetAttributeString("tag", ""), true, args);
LocalizedString tutorialText = TextManager.GetFormatted(segments[index].TextContent.Value.Tag, args);
tutorialText = TextManager.ParseInputTypes(tutorialText);
string objectiveText = string.Empty;
LocalizedString objectiveText = string.Empty;
if (!string.IsNullOrEmpty(activeContentSegment.Objective))
if (!segments[index].Objective.IsNullOrEmpty())
{
if (args.Length == 0)
{
objectiveText = activeContentSegment.Objective;
objectiveText = segments[index].Objective;
}
else
{
objectiveText = string.Format(activeContentSegment.Objective, args);
objectiveText = TextManager.GetFormatted(segments[index].Objective, args);
}
objectiveText = TextManager.ParseInputTypes(objectiveText);
activeContentSegment.Objective = objectiveText;
segments[index].Objective = objectiveText;
}
else
{
activeContentSegment.IsTriggered = true; // Complete at this stage only if no related objective
segments[index].IsTriggered = true; // Complete at this stage only if no related objective
}
switch (activeContentSegment.ContentType)
switch (segments[index].ContentType)
{
case TutorialContentTypes.None:
case TutorialContentType.None:
break;
case TutorialContentTypes.Video:
case TutorialContentType.Video:
infoBox = CreateInfoFrame(TextManager.Get(activeContentSegment.Id), tutorialText,
activeContentSegment.TextContent.GetAttributeInt("width", 300),
activeContentSegment.TextContent.GetAttributeInt("height", 80),
activeContentSegment.TextContent.GetAttributeString("anchor", "Center"), true, () => LoadVideo(activeContentSegment));
activeContentSegment.TextContent.Value.Width,
activeContentSegment.TextContent.Value.Height,
activeContentSegment.TextContent.Value.Anchor, true, () => LoadVideo(activeContentSegment));
break;
case TutorialContentTypes.ManualVideo:
case TutorialContentType.ManualVideo:
infoBox = CreateInfoFrame(TextManager.Get(activeContentSegment.Id), tutorialText,
activeContentSegment.TextContent.GetAttributeInt("width", 300),
activeContentSegment.TextContent.GetAttributeInt("height", 80),
activeContentSegment.TextContent.GetAttributeString("anchor", "Center"), true, StopCurrentContentSegment, () => LoadVideo(activeContentSegment));
activeContentSegment.TextContent.Value.Width,
activeContentSegment.TextContent.Value.Height,
activeContentSegment.TextContent.Value.Anchor, true, StopCurrentContentSegment, () => LoadVideo(activeContentSegment));
break;
case TutorialContentTypes.TextOnly:
case TutorialContentType.TextOnly:
infoBox = CreateInfoFrame(TextManager.Get(activeContentSegment.Id), tutorialText,
activeContentSegment.TextContent.GetAttributeInt("width", 300),
activeContentSegment.TextContent.GetAttributeInt("height", 80),
activeContentSegment.TextContent.GetAttributeString("anchor", "Center"), true, StopCurrentContentSegment);
activeContentSegment.TextContent.Value.Width,
activeContentSegment.TextContent.Value.Height,
activeContentSegment.TextContent.Value.Anchor, true, StopCurrentContentSegment);
break;
}
}
public virtual void Stop()
{
started = ContentRunning = Initialized = false;
ContentRunning = false;
infoBox = null;
if (videoPlayer != null)
{
videoPlayer.Remove();
videoPlayer = null;
}
videoPlayer.Remove();
}
#endregion
@@ -334,50 +268,51 @@ namespace Barotrauma.Tutorials
for (int i = 0; i < activeObjectives.Count; i++)
{
CreateObjectiveGUI(activeObjectives[i], i, activeObjectives[i].ContentType);
CreateObjectiveGUI(activeObjectives[i], i, segments[activeObjectives[i]].ContentType);
}
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
windowMode = GameMain.Config.WindowMode;
windowMode = GameSettings.CurrentConfig.Graphics.DisplayMode;
prevUIScale = GUI.Scale;
}
protected void StopCurrentContentSegment()
{
if (!string.IsNullOrEmpty(activeContentSegment.Objective))
if (!activeContentSegment.Objective.IsNullOrEmpty())
{
AddNewObjective(activeContentSegment, activeContentSegment.ContentType);
AddNewObjective(activeContentSegmentIndex, activeContentSegment.ContentType);
}
activeContentSegment = null;
ContentRunning = false;
activeContentSegmentIndex = Index.End;
}
protected virtual void CheckActiveObjectives(TutorialSegment objective, float deltaTime)
protected virtual void CheckActiveObjectives(Index objective, float deltaTime)
{
}
protected bool HasObjective(TutorialSegment segment)
protected bool HasObjective(Index segment)
{
return activeObjectives.Contains(segment);
}
protected void AddNewObjective(TutorialSegment segment, TutorialContentTypes type)
protected void AddNewObjective(Index segment, TutorialContentType type)
{
activeObjectives.Add(segment);
CreateObjectiveGUI(segment, activeObjectives.Count - 1, type);
}
private void CreateObjectiveGUI(TutorialSegment segment, int index, TutorialContentTypes type)
private void CreateObjectiveGUI(Index segmentIndex, int index, TutorialContentType type)
{
string objectiveText = TextManager.ParseInputTypes(segment.Objective);
Point replayButtonSize = new Point((int)(GUI.LargeFont.MeasureString(objectiveText).X), (int)(GUI.LargeFont.MeasureString(objectiveText).Y * 1.45f));
var segment = segments[segmentIndex];
LocalizedString objectiveText = TextManager.ParseInputTypes(segment.Objective);
Point replayButtonSize = new Point((int)(GUIStyle.LargeFont.MeasureString(objectiveText).X), (int)(GUIStyle.LargeFont.MeasureString(objectiveText).Y * 1.45f));
segment.ReplayButton = new GUIButton(new RectTransform(replayButtonSize, objectiveFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft) { AbsoluteOffset = new Point(0, (replayButtonSize.Y + (int)(20f * GUI.Scale)) * index) }, style: null);
segment.ReplayButton.OnClicked += (GUIButton btn, object userdata) =>
{
if (type == TutorialContentTypes.Video)
if (type == TutorialContentType.Video)
{
ReplaySegmentVideo(segment);
}
@@ -388,23 +323,23 @@ namespace Barotrauma.Tutorials
return true;
};
string objectiveTitleText = TextManager.ParseInputTypes(objectiveTranslated);
int yOffset = (int)((GUI.SubHeadingFont.MeasureString(objectiveTitleText).Y + 5));
segment.LinkedTitle = new GUITextBlock(new RectTransform(new Point((int)GUI.SubHeadingFont.MeasureString(objectiveTitleText).X, yOffset), segment.ReplayButton.RectTransform, Anchor.CenterLeft, Pivot.BottomLeft) /*{ AbsoluteOffset = new Point((int)(-10 * GUI.Scale), 0) }*/,
objectiveTitleText, textColor: Color.White, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
LocalizedString objectiveTitleText = TextManager.ParseInputTypes(objectiveTranslated);
int yOffset = (int)((GUIStyle.SubHeadingFont.MeasureString(objectiveTitleText).Y + 5));
segment.LinkedTitle = new GUITextBlock(new RectTransform(new Point((int)GUIStyle.SubHeadingFont.MeasureString(objectiveTitleText).X, yOffset), segment.ReplayButton.RectTransform, Anchor.CenterLeft, Pivot.BottomLeft) /*{ AbsoluteOffset = new Point((int)(-10 * GUI.Scale), 0) }*/,
objectiveTitleText, textColor: Color.White, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft)
{
ForceUpperCase = true
ForceUpperCase = ForceUpperCase.Yes
};
segment.LinkedText = new GUITextBlock(new RectTransform(new Point((int)GUI.LargeFont.MeasureString(objectiveText).X, yOffset), segment.ReplayButton.RectTransform, Anchor.CenterLeft, Pivot.TopLeft) /*{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), 0) }*/,
objectiveText, textColor: new Color(4, 180, 108), font: GUI.LargeFont, textAlignment: Alignment.CenterLeft);
segment.LinkedText = new GUITextBlock(new RectTransform(new Point((int)GUIStyle.LargeFont.MeasureString(objectiveText).X, yOffset), segment.ReplayButton.RectTransform, Anchor.CenterLeft, Pivot.TopLeft) /*{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), 0) }*/,
objectiveText, textColor: new Color(4, 180, 108), font: GUIStyle.LargeFont, textAlignment: Alignment.CenterLeft);
segment.LinkedTitle.Color = segment.LinkedTitle.HoverColor = segment.LinkedTitle.PressedColor = segment.LinkedTitle.SelectedColor = Color.Transparent;
segment.LinkedText.Color = segment.LinkedText.HoverColor = segment.LinkedText.PressedColor = segment.LinkedText.SelectedColor = Color.Transparent;
segment.ReplayButton.Color = segment.ReplayButton.HoverColor = segment.ReplayButton.PressedColor = segment.ReplayButton.SelectedColor = Color.Transparent;
}
private void ReplaySegmentVideo(TutorialSegment segment)
private void ReplaySegmentVideo(Segment segment)
{
if (ContentRunning) return;
Inventory.DraggingItems.Clear();
@@ -413,30 +348,31 @@ namespace Barotrauma.Tutorials
//videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(segment.VideoContent), new VideoPlayer.TextSettings(segment.VideoContent), segment.Id, true, callback: () => ContentRunning = false);
}
private void ShowSegmentText(TutorialSegment segment)
private void ShowSegmentText(Segment segment)
{
if (ContentRunning) return;
Inventory.DraggingItems.Clear();
ContentRunning = true;
string tutorialText = TextManager.GetFormatted(segment.TextContent.GetAttributeString("tag", ""), true, segment.Args);
LocalizedString tutorialText = TextManager.GetFormatted(segment.TextContent.Value.Tag, segment.Args);
Action videoAction = null;
if (segment.ContentType != TutorialContentTypes.TextOnly)
if (segment.ContentType != TutorialContentType.TextOnly)
{
videoAction = () => LoadVideo(segment);
}
infoBox = CreateInfoFrame(TextManager.Get(segment.Id), tutorialText,
segment.TextContent.GetAttributeInt("width", 300),
segment.TextContent.GetAttributeInt("height", 80),
segment.TextContent.GetAttributeString("anchor", "Center"), true, () => ContentRunning = false, videoAction);
segment.TextContent.Value.Width,
segment.TextContent.Value.Height,
segment.TextContent.Value.Anchor, true, () => ContentRunning = false, videoAction);
}
protected void RemoveCompletedObjective(TutorialSegment segment)
protected void RemoveCompletedObjective(Index segmentIndex)
{
if (!HasObjective(segment)) return;
if (!HasObjective(segmentIndex)) return;
var segment = segments[segmentIndex];
segment.IsTriggered = true;
segment.ReplayButton.OnClicked = null;
@@ -467,18 +403,20 @@ namespace Barotrauma.Tutorials
GUIImage stroke = new GUIImage(rectTB, "Stroke");
stroke.Color = stroke.SelectedColor = stroke.HoverColor = stroke.PressedColor = color;
CoroutineManager.StartCoroutine(WaitForObjectiveEnd(segment));
CoroutineManager.StartCoroutine(WaitForObjectiveEnd(segmentIndex));
}
private IEnumerable<CoroutineStatus> WaitForObjectiveEnd(TutorialSegment objective)
private IEnumerable<CoroutineStatus> WaitForObjectiveEnd(Index objectiveIndex)
{
var objective = segments[objectiveIndex];
yield return new WaitForSeconds(2.0f);
objectiveFrame.RemoveChild(objective.ReplayButton);
activeObjectives.Remove(objective);
activeObjectives.Remove(objectiveIndex);
for (int i = 0; i < activeObjectives.Count; i++)
{
activeObjectives[i].ReplayButton.RectTransform.AbsoluteOffset = new Point(0, (activeObjectives[i].ReplayButton.Rect.Height + 20) * i);
var activeObjective = segments[activeObjectives[i]];
activeObjective.ReplayButton.RectTransform.AbsoluteOffset = new Point(0, (activeObjective.ReplayButton.Rect.Height + 20) * i);
}
}
@@ -492,32 +430,25 @@ namespace Barotrauma.Tutorials
return true;
}
protected GUIComponent CreateInfoFrame(string title, string text, int width = 300, int height = 80, string anchorStr = "", bool hasButton = false, Action callback = null, Action showVideo = null)
protected GUIComponent CreateInfoFrame(LocalizedString title, LocalizedString text, int width = 300, int height = 80, Anchor anchor = Anchor.TopRight, bool hasButton = false, Action callback = null, Action showVideo = null)
{
if (hasButton) height += 60;
Anchor anchor = Anchor.TopRight;
if (anchorStr != string.Empty)
{
Enum.TryParse(anchorStr, out anchor);
}
width = (int)(width * GUI.Scale);
height = (int)(height * GUI.Scale);
string wrappedText = ToolBox.WrapText(text, width, GUI.Font);
height += (int)GUI.Font.MeasureString(wrappedText).Y;
LocalizedString wrappedText = ToolBox.WrapText(text, width, GUIStyle.Font);
height += (int)GUIStyle.Font.MeasureString(wrappedText).Y;
if (title.Length > 0)
{
height += (int)GUI.Font.MeasureString(title).Y + (int)(150 * GUI.Scale);
height += (int)GUIStyle.Font.MeasureString(title).Y + (int)(150 * GUI.Scale);
}
var background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker");
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), background.RectTransform, anchor));
infoBlock.Flash(GUI.Style.Green);
infoBlock.Flash(GUIStyle.Green);
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), infoBlock.RectTransform, Anchor.Center))
{
@@ -528,20 +459,12 @@ namespace Barotrauma.Tutorials
if (title.Length > 0)
{
var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform),
title, font: GUI.LargeFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0));
title, font: GUIStyle.LargeFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0));
titleBlock.RectTransform.IsFixedSize = true;
}
List<RichTextData> richTextData = RichTextData.GetRichTextData(" " + text, out text);
GUITextBlock textBlock;
if (richTextData == null)
{
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), text, wrap: true);
}
else
{
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), richTextData, text, wrap: true);
}
text = RichString.Rich(text);
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), text, wrap: true);
textBlock.RectTransform.IsFixedSize = true;
infoBoxClosedCallback = callback;
@@ -589,22 +512,26 @@ namespace Barotrauma.Tutorials
#endregion
#region Video
protected void LoadVideo(TutorialSegment segment)
protected void LoadVideo(Segment segment)
{
if (videoPlayer == null) videoPlayer = new VideoPlayer();
if (segment.ContentType != TutorialContentTypes.ManualVideo)
if (segment.ContentType != TutorialContentType.ManualVideo)
{
videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(segment.VideoContent), new VideoPlayer.TextSettings(segment.VideoContent), segment.Id, true, segment.Objective, StopCurrentContentSegment);
videoPlayer.LoadContent(
PlayableContentPath,
new VideoPlayer.VideoSettings(segment.VideoContent.Value.File),
new VideoPlayer.TextSettings(segment.VideoContent.Value.TextTag, segment.VideoContent.Value.Width),
segment.Id, true, segment.Objective, StopCurrentContentSegment);
}
else
{
videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(segment.VideoContent), null, segment.Id, true, string.Empty, null);
videoPlayer.LoadContent(PlayableContentPath, new VideoPlayer.VideoSettings(segment.VideoContent.Value.File), null, segment.Id, true, string.Empty, null);
}
}
#endregion
#region Highlights
protected void HighlightInventorySlot(Inventory inventory, string identifier, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount)
protected void HighlightInventorySlot(Inventory inventory, Identifier identifier, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount)
{
if (inventory.visualSlots == null) { return; }
for (int i = 0; i < inventory.Capacity; i++)
@@ -616,7 +543,7 @@ namespace Barotrauma.Tutorials
}
}
protected void HighlightInventorySlotWithTag(Inventory inventory, string tag, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount)
protected void HighlightInventorySlotWithTag(Inventory inventory, Identifier tag, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount)
{
if (inventory.visualSlots == null) { return; }
for (int i = 0; i < inventory.Capacity; i++)

View File

@@ -5,11 +5,6 @@ namespace Barotrauma
class TutorialMode : GameMode
{
public Tutorial Tutorial;
public static void StartTutorial(Tutorial tutorial)
{
tutorial.Initialize();
}
public TutorialMode(GameModePreset preset)
: base(preset)
@@ -20,7 +15,6 @@ namespace Barotrauma
{
base.Start();
GameMain.GameSession.CrewManager = new CrewManager(true);
Tutorial.Start();
foreach (Item item in Item.ItemList)
{
//don't consider the items to belong in the outpost to prevent the stealing icon from showing

View File

@@ -32,6 +32,7 @@ namespace Barotrauma
}
else
{
tabMenu?.OnClose();
tabMenu = null;
NetLobbyScreen.JobInfoFrame = null;
}
@@ -64,12 +65,12 @@ namespace Barotrauma
GameMain.Instance.ResolutionChanged -= CreateTopLeftButtons;
};
int buttonHeight = GUI.IntScale(40);
Vector2 buttonSpriteSize = GUI.Style.GetComponentStyle("CrewListToggleButton").GetDefaultSprite().size;
Vector2 buttonSpriteSize = GUIStyle.GetComponentStyle("CrewListToggleButton").GetDefaultSprite().size;
int buttonWidth = (int)((buttonHeight / buttonSpriteSize.Y) * buttonSpriteSize.X);
Point buttonSize = new Point(buttonWidth, buttonHeight);
crewListButton = new GUIButton(new RectTransform(buttonSize, parent: topLeftButtonGroup.RectTransform), style: "CrewListToggleButton")
{
ToolTip = TextManager.GetWithVariable("hudbutton.crewlist", "[key]", GameMain.Config.KeyBindText(InputType.CrewOrders)),
ToolTip = TextManager.GetWithVariable("hudbutton.crewlist", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.CrewOrders)),
OnClicked = (GUIButton btn, object userdata) =>
{
if (CrewManager == null) { return false; }
@@ -79,7 +80,7 @@ namespace Barotrauma
};
commandButton = new GUIButton(new RectTransform(buttonSize, parent: topLeftButtonGroup.RectTransform), style: "CommandButton")
{
ToolTip = TextManager.GetWithVariable("hudbutton.commandinterface", "[key]", GameMain.Config.KeyBindText(InputType.Command)),
ToolTip = TextManager.GetWithVariable("hudbutton.commandinterface", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command)),
OnClicked = (button, userData) =>
{
if (CrewManager == null) { return false; }
@@ -89,7 +90,7 @@ namespace Barotrauma
};
tabMenuButton = new GUIButton(new RectTransform(buttonSize, parent: topLeftButtonGroup.RectTransform), style: "TabMenuButton")
{
ToolTip = TextManager.GetWithVariable("hudbutton.tabmenu", "[key]", GameMain.Config.KeyBindText(InputType.InfoTab)),
ToolTip = TextManager.GetWithVariable("hudbutton.tabmenu", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.InfoTab)),
OnClicked = (button, userData) => ToggleTabMenu()
};
@@ -190,7 +191,7 @@ namespace Barotrauma
}
else
{
tabMenu.Update();
tabMenu.Update(deltaTime);
if ((PlayerInput.KeyHit(InputType.InfoTab) || PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) &&
!(GUI.KeyboardDispatcher.Subscriber is GUITextBox))
{

View File

@@ -13,14 +13,14 @@ namespace Barotrauma
{
private const string HintManagerFile = "hintmanager.xml";
public static bool Enabled => GameMain.Config != null && !GameMain.Config.DisableInGameHints;
private static HashSet<string> HintIdentifiers { get; set; }
private static Dictionary<string, HashSet<string>> HintTags { get; } = new Dictionary<string, HashSet<string>>();
private static Dictionary<string, (string identifier, string option)> HintOrders { get; } = new Dictionary<string, (string orderIdentifier, string orderOption)>();
public static bool Enabled => !GameSettings.CurrentConfig.DisableInGameHints;
private static HashSet<Identifier> HintIdentifiers { get; set; }
private static Dictionary<Identifier, HashSet<Identifier>> HintTags { get; } = new Dictionary<Identifier, HashSet<Identifier>>();
private static Dictionary<Identifier, (Identifier identifier, Identifier option)> HintOrders { get; } = new Dictionary<Identifier, (Identifier orderIdentifier, Identifier orderOption)>();
/// <summary>
/// Hints that have already been shown this round and shouldn't be shown shown again until the next round
/// </summary>
private static HashSet<string> HintsIgnoredThisRound { get; } = new HashSet<string>();
private static HashSet<Identifier> HintsIgnoredThisRound { get; } = new HashSet<Identifier>();
private static GUIMessageBox ActiveHintMessageBox { get; set; }
private static Action OnUpdate { get; set; }
private static double TimeStoppedInteracting { get; set; }
@@ -43,10 +43,10 @@ namespace Barotrauma
var doc = XMLExtensions.TryLoadXml(HintManagerFile);
if (doc?.Root != null)
{
HintIdentifiers = new HashSet<string>();
HintIdentifiers = new HashSet<Identifier>();
foreach (var element in doc.Root.Elements())
{
GetHintsRecursive(element, element.Name.ToString());
GetHintsRecursive(element, element.NameAsIdentifier());
}
}
else
@@ -59,18 +59,18 @@ namespace Barotrauma
DebugConsole.ThrowError($"File \"{HintManagerFile}\" is missing - cannot initialize the HintManager!");
}
static void GetHintsRecursive(XElement element, string identifier)
static void GetHintsRecursive(XElement element, Identifier identifier)
{
if (!element.HasElements)
{
HintIdentifiers.Add(identifier);
if (element.GetAttributeStringArray("tags", null, convertToLowerInvariant: true) is string[] tags)
if (element.GetAttributeIdentifierArray("tags", null) is Identifier[] tags)
{
HintTags.TryAdd(identifier, tags.ToHashSet());
}
if (element.GetAttributeString("order", null) is string orderIdentifier && !string.IsNullOrEmpty(orderIdentifier))
if (element.GetAttributeIdentifier("order", Identifier.Empty) is Identifier orderIdentifier && orderIdentifier != Identifier.Empty)
{
string orderOption = element.GetAttributeString("orderoption", "");
Identifier orderOption = element.GetAttributeIdentifier("orderoption", Identifier.Empty);
HintOrders.Add(identifier, (orderIdentifier, orderOption));
}
return;
@@ -82,14 +82,14 @@ namespace Barotrauma
}
foreach (var childElement in element.Elements())
{
GetHintsRecursive(childElement, $"{identifier}.{childElement.Name}");
GetHintsRecursive(childElement, $"{identifier}.{childElement.Name}".ToIdentifier());
}
}
}
public static void Update()
{
if (HintIdentifiers == null || GameMain.Config.DisableInGameHints) { return; }
if (HintIdentifiers == null || GameSettings.CurrentConfig.DisableInGameHints) { return; }
if (GameMain.GameSession == null || !GameMain.GameSession.IsRunning) { return; }
if (ActiveHintMessageBox != null)
@@ -137,7 +137,7 @@ namespace Barotrauma
// onstartedinteracting.brokenitem
if (item.Repairables.Any(r => r.IsBelowRepairThreshold))
{
if (DisplayHint($"{hintIdentifierBase}.brokenitem")) { return; }
if (DisplayHint($"{hintIdentifierBase}.brokenitem".ToIdentifier())) { return; }
}
// Don't display other item-related hints if the repair interface is displayed
@@ -147,22 +147,25 @@ namespace Barotrauma
if (item.Submarine?.Info?.Type == SubmarineType.Outpost &&
item.ContainedItems.Any(i => !i.AllowStealing))
{
if (DisplayHint($"{hintIdentifierBase}.lootingisstealing")) { return; }
if (DisplayHint($"{hintIdentifierBase}.lootingisstealing".ToIdentifier())) { return; }
}
// onstartedinteracting.turretperiscope
if (item.HasTag("periscope") &&
item.GetConnectedComponents<Turret>().FirstOrDefault(t => t.Item.HasTag("turret")) is Turret)
{
if (DisplayHint($"{hintIdentifierBase}.turretperiscope",
variableTags: new string[] { "[shootkey]", "[deselectkey]", },
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect) }))
if (DisplayHint($"{hintIdentifierBase}.turretperiscope".ToIdentifier(),
variables: new[]
{
("[shootkey]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot)),
("[deselectkey]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect))
}))
{ return; }
}
// onstartedinteracting.item...
hintIdentifierBase += ".item";
foreach (string hintIdentifier in HintIdentifiers)
foreach (Identifier hintIdentifier in HintIdentifiers)
{
if (!hintIdentifier.StartsWith(hintIdentifierBase)) { continue; }
if (!HintTags.TryGetValue(hintIdentifier, out var hintTags)) { continue; }
@@ -180,7 +183,7 @@ namespace Barotrauma
Character.Controlled.SelectedConstruction.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
containedItems.Count(i => i.HasTag("reactorfuel")) > 1)
{
if (DisplayHint("onisinteracting.reactorwithextrarods")) { return; }
if (DisplayHint("onisinteracting.reactorwithextrarods".ToIdentifier())) { return; }
}
}
@@ -233,11 +236,11 @@ namespace Barotrauma
}
if (!GameMain.GameSession.GameMode.IsSinglePlayer &&
GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Disabled)
GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Disabled)
{
DisplayHint("onroundstarted.voipdisabled", onUpdate: () =>
DisplayHint("onroundstarted.voipdisabled".ToIdentifier(), onUpdate: () =>
{
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Disabled) { return; }
if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Disabled) { return; }
ActiveHintMessageBox.Close();
});
}
@@ -271,7 +274,7 @@ namespace Barotrauma
if (spottedCharacter == null || spottedCharacter.Removed || spottedCharacter.IsDead) { return; }
if (Character.Controlled.SelectedConstruction != sonar) { return; }
if (HumanAIController.IsFriendly(Character.Controlled, spottedCharacter)) { return; }
DisplayHint("onsonarspottedenemy");
DisplayHint("onsonarspottedenemy".ToIdentifier());
}
public static void OnAfflictionDisplayed(Character character, List<Affliction> displayedAfflictions)
@@ -285,9 +288,8 @@ namespace Barotrauma
if (affliction.Prefab == AfflictionPrefab.OxygenLow) { continue; }
if (affliction.Prefab == AfflictionPrefab.RadiationSickness && (GameMain.GameSession.Map?.Radiation?.IsEntityRadiated(character) ?? false)) { continue; }
if (affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
DisplayHint("onafflictiondisplayed",
variableTags: new string[1] { "[key]" },
variableValues: new string[1] { GameMain.Config.KeyBindText(InputType.Health) },
DisplayHint("onafflictiondisplayed".ToIdentifier(),
variables: new[] { ("[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)) },
icon: affliction.Prefab.Icon,
iconColor: CharacterHealth.GetAfflictionIconColor(affliction),
onUpdate: () =>
@@ -308,12 +310,11 @@ namespace Barotrauma
if (TimeStoppedInteracting + 1 > Timing.TotalTime) { return; }
if (GUI.MouseOn != null) { return; }
if (Character.Controlled.Inventory?.visualSlots != null && Character.Controlled.Inventory.visualSlots.Any(s => s.InteractRect.Contains(PlayerInput.MousePosition))) { return; }
string hintIdentifier = "onshootwithoutaiming";
Identifier hintIdentifier = "onshootwithoutaiming".ToIdentifier();
if (!HintTags.TryGetValue(hintIdentifier, out var tags)) { return; }
if (!item.HasTag(tags)) { return; }
DisplayHint(hintIdentifier,
variableTags: new string[1] { "[key]" },
variableValues: new string[1] { GameMain.Config.KeyBindText(InputType.Aim) },
variables: new[] { ("[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim)) },
onUpdate: () =>
{
if (character.SelectedConstruction == null && GUI.MouseOn == null && PlayerInput.KeyDown(InputType.Aim))
@@ -328,21 +329,21 @@ namespace Barotrauma
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled) { return; }
if (door == null || door.Stuck < 20.0f) { return; }
DisplayHint("onweldingdoor");
DisplayHint("onweldingdoor".ToIdentifier());
}
public static void OnTryOpenStuckDoor(Character character)
{
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled) { return; }
DisplayHint("ontryopenstuckdoor");
DisplayHint("ontryopenstuckdoor".ToIdentifier());
}
public static void OnShowCampaignInterface(CampaignMode.InteractionType interactionType)
{
if (!CanDisplayHints()) { return; }
if (interactionType == CampaignMode.InteractionType.None) { return; }
string hintIdentifier = $"onshowcampaigninterface.{interactionType.ToString().ToLowerInvariant()}";
Identifier hintIdentifier = $"onshowcampaigninterface.{interactionType}".ToIdentifier();
DisplayHint(hintIdentifier, onUpdate: () =>
{
@@ -359,7 +360,7 @@ namespace Barotrauma
{
IgnoreReminder("commandinterface");
if (!CanDisplayHints()) { return; }
DisplayHint("onshowcommandinterface", onUpdate: () =>
DisplayHint("onshowcommandinterface".ToIdentifier(), onUpdate: () =>
{
if (CrewManager.IsCommandInterfaceOpen) { return; }
ActiveHintMessageBox.Close();
@@ -370,7 +371,7 @@ namespace Barotrauma
{
if (!CanDisplayHints()) { return; }
if (CharacterHealth.OpenHealthWindow == null) { return; }
DisplayHint("onshowhealthinterface", onUpdate: () =>
DisplayHint("onshowhealthinterface".ToIdentifier(), onUpdate: () =>
{
if (CharacterHealth.OpenHealthWindow != null) { return; }
ActiveHintMessageBox.Close();
@@ -387,7 +388,7 @@ namespace Barotrauma
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled) { return; }
if (item == null || item.AllowStealing || !item.StolenDuringRound) { return; }
DisplayHint("onstoleitem", onUpdate: () =>
DisplayHint("onstoleitem".ToIdentifier(), onUpdate: () =>
{
if (item == null || item.Removed || item.GetRootInventoryOwner() != character)
{
@@ -400,7 +401,7 @@ namespace Barotrauma
{
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled || !character.LockHands) { return; }
DisplayHint("onhandcuffed", onUpdate: () =>
DisplayHint("onhandcuffed".ToIdentifier(), onUpdate: () =>
{
if (character != null && !character.Removed && character.LockHands) { return; }
ActiveHintMessageBox.Close();
@@ -413,7 +414,7 @@ namespace Barotrauma
if (reactor == null) { return; }
if (reactor.Item.Submarine?.Info?.Type != SubmarineType.Player || reactor.Item.Submarine.TeamID != Character.Controlled.TeamID) { return; }
if (!HasValidJob("engineer")) { return; }
DisplayHint("onreactoroutoffuel", onUpdate: () =>
DisplayHint("onreactoroutoffuel".ToIdentifier(), onUpdate: () =>
{
if (reactor?.Item != null && !reactor.Item.Removed && reactor.AvailableFuel < 1) { return; }
ActiveHintMessageBox.Close();
@@ -424,13 +425,13 @@ namespace Barotrauma
{
if (!CanDisplayHints()) { return; }
if (transitionType == CampaignMode.TransitionType.None) { return; }
DisplayHint($"onavailabletransition.{transitionType.ToString().ToLowerInvariant()}");
DisplayHint($"onavailabletransition.{transitionType}".ToIdentifier());
}
public static void OnShowSubInventory(Item item)
{
if (item?.Prefab == null) { return; }
if (item.Prefab.Identifier.Equals("toolbelt", StringComparison.OrdinalIgnoreCase))
if (item.Prefab.Identifier == "toolbelt")
{
IgnoreReminder("toolbelt");
}
@@ -447,7 +448,7 @@ namespace Barotrauma
if (character != Character.Controlled) { return; }
if (character.IsDead) { return; }
if (character.CharacterHealth != null && character.Vitality < character.CharacterHealth.MinVitality) { return; }
DisplayHint("oncharacterunconscious");
DisplayHint("oncharacterunconscious".ToIdentifier());
}
public static void OnCharacterKilled(Character character)
@@ -457,21 +458,21 @@ namespace Barotrauma
if (GameMain.IsMultiplayer) { return; }
if (GameMain.GameSession?.CrewManager == null) { return; }
if (GameMain.GameSession.CrewManager.GetCharacters().None(c => !c.IsDead)) { return; }
DisplayHint("oncharacterkilled");
DisplayHint("oncharacterkilled".ToIdentifier());
}
private static void OnStartedControlling()
{
if (Level.IsLoadedOutpost) { return; }
if (Character.Controlled?.Info?.Job?.Prefab == null) { return; }
string hintIdentifier = $"onstartedcontrolling.job.{Character.Controlled.Info.Job.Prefab.Identifier}";
Identifier hintIdentifier = $"onstartedcontrolling.job.{Character.Controlled.Info.Job.Prefab.Identifier}".ToIdentifier();
DisplayHint(hintIdentifier,
icon: Character.Controlled.Info.Job.Prefab.Icon,
iconColor: Character.Controlled.Info.Job.Prefab.UIColor,
onDisplay: () =>
{
if (!HintOrders.TryGetValue(hintIdentifier, out var orderInfo)) { return; }
var orderPrefab = Order.GetPrefab(orderInfo.identifier);
var orderPrefab = OrderPrefab.Prefabs[orderInfo.identifier];
if (orderPrefab == null) { return; }
Item targetEntity = null;
ItemComponent targetItem = null;
@@ -481,8 +482,8 @@ namespace Barotrauma
if (targetEntity == null) { return; }
targetItem = orderPrefab.GetTargetItemComponent(targetEntity);
}
var order = new Order(orderPrefab, targetEntity as Entity, targetItem, orderGiver: Character.Controlled);
GameMain.GameSession?.CrewManager?.SetCharacterOrder(Character.Controlled, order, orderInfo.option, CharacterInfo.HighestManualOrderPriority, Character.Controlled);
var order = new Order(orderPrefab, orderInfo.option, targetEntity, targetItem, orderGiver: Character.Controlled).WithManualPriority(CharacterInfo.HighestManualOrderPriority);
GameMain.GameSession.CrewManager.SetCharacterOrder(Character.Controlled, order);
});
}
@@ -498,7 +499,7 @@ namespace Barotrauma
if (!steering.SteeringPath.Finished && steering.SteeringPath.NextNode != null) { return; }
if (steering.LevelStartSelected && (Level.Loaded.StartOutpost == null || !steering.Item.Submarine.AtStartExit)) { return; }
if (steering.LevelEndSelected && (Level.Loaded.EndOutpost == null || !steering.Item.Submarine.AtEndExit)) { return; }
DisplayHint("onautopilotreachedoutpost");
DisplayHint("onautopilotreachedoutpost".ToIdentifier());
}
public static void OnStatusEffectApplied(ItemComponent component, ActionType actionType, Character character)
@@ -507,7 +508,7 @@ namespace Barotrauma
if (character != Character.Controlled) { return; }
// Could make this more generic if there will ever be any other status effect related hints
if (!(component is Repairable) || actionType != ActionType.OnFailure) { return; }
DisplayHint("onrepairfailed");
DisplayHint("onrepairfailed".ToIdentifier());
}
public static void OnActiveOrderAdded(Order order)
@@ -519,7 +520,7 @@ namespace Barotrauma
order.TargetEntity is Hull h &&
h.Submarine?.TeamID == Character.Controlled.TeamID)
{
DisplayHint("onballastflorainfected");
DisplayHint("onballastflorainfected".ToIdentifier());
}
}
@@ -529,7 +530,7 @@ namespace Barotrauma
var divingGear = Character.Controlled.GetEquippedItem("diving", InvSlotType.OuterClothes);
if (divingGear?.OwnInventory == null) { return; }
if (divingGear.GetContainedItemConditionPercentage() > 0.0f) { return; }
DisplayHint("ondivinggearoutofoxygen", onUpdate: () =>
DisplayHint("ondivinggearoutofoxygen".ToIdentifier(), onUpdate: () =>
{
if (divingGear == null || divingGear.Removed ||
Character.Controlled == null || !Character.Controlled.HasEquippedItem(divingGear) ||
@@ -546,7 +547,7 @@ namespace Barotrauma
if (Character.Controlled.CurrentHull == null) { return; }
if (HumanAIController.IsBallastFloraNoticeable(Character.Controlled, Character.Controlled.CurrentHull))
{
if (IsOnFriendlySub() && DisplayHint("onballastflorainfected")) { return; }
if (IsOnFriendlySub() && DisplayHint("onballastflorainfected".ToIdentifier())) { return; }
}
foreach (var gap in Character.Controlled.CurrentHull.ConnectedGaps)
{
@@ -556,7 +557,7 @@ namespace Barotrauma
{
if (!IsWearingDivingSuit()) { continue; }
if (Character.Controlled.IsProtectedFromPressure()) { continue; }
if (DisplayHint("divingsuitwarning", extendTextTag: false)) { return; }
if (DisplayHint("divingsuitwarning".ToIdentifier(), extendTextTag: false)) { return; }
continue;
}
foreach (var me in gap.linkedTo)
@@ -565,8 +566,8 @@ namespace Barotrauma
if (!(me is Hull adjacentHull)) { continue; }
if (!IsOnFriendlySub()) { continue; }
if (IsWearingDivingSuit()) { continue; }
if (adjacentHull.LethalPressure > 5.0f && DisplayHint("onadjacenthull.highpressure")) { return; }
if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && DisplayHint("onadjacenthull.highwaterpercentage")) { return; }
if (adjacentHull.LethalPressure > 5.0f && DisplayHint("onadjacenthull.highpressure".ToIdentifier())) { return; }
if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && DisplayHint("onadjacenthull.highwaterpercentage".ToIdentifier())) { return; }
}
static bool IsWearingDivingSuit() => Character.Controlled.GetEquippedItem("deepdiving", InvSlotType.OuterClothes) is Item;
@@ -586,7 +587,7 @@ namespace Barotrauma
if (GameMain.GameSession.GameMode.IsSinglePlayer)
{
if (DisplayHint($"{hintIdentifierBase}.characterchange"))
if (DisplayHint($"{hintIdentifierBase}.characterchange".ToIdentifier()))
{
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
return;
@@ -595,9 +596,8 @@ namespace Barotrauma
if (Level.Loaded.Type != LevelData.LevelType.Outpost)
{
if (DisplayHint($"{hintIdentifierBase}.commandinterface",
variableTags: new string[] { "[commandkey]" },
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.Command) },
if (DisplayHint($"{hintIdentifierBase}.commandinterface".ToIdentifier(),
variables: new[] { ("[commandkey]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command)) },
onUpdate: () =>
{
if (!CrewManager.IsCommandInterfaceOpen) { return; }
@@ -609,9 +609,8 @@ namespace Barotrauma
}
}
if (DisplayHint($"{hintIdentifierBase}.tabmenu",
variableTags: new string[] { "[infotabkey]" },
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.InfoTab) },
if (DisplayHint($"{hintIdentifierBase}.tabmenu".ToIdentifier(),
variables: new[] { ("[infotabkey]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.InfoTab)) },
onUpdate: () =>
{
if (!GameSession.IsTabMenuOpen) { return; }
@@ -624,7 +623,7 @@ namespace Barotrauma
if (Character.Controlled.Inventory?.GetItemInLimbSlot(InvSlotType.Bag)?.Prefab?.Identifier == "toolbelt")
{
if (DisplayHint($"{hintIdentifierBase}.toolbelt"))
if (DisplayHint($"{hintIdentifierBase}.toolbelt".ToIdentifier()))
{
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
return;
@@ -632,25 +631,25 @@ namespace Barotrauma
}
}
private static bool DisplayHint(string hintIdentifier, bool extendTextTag = true, string[] variableTags = null, string[] variableValues = null, Sprite icon = null, Color? iconColor = null, Action onDisplay = null, Action onUpdate = null)
private static bool DisplayHint(Identifier hintIdentifier, bool extendTextTag = true, (Identifier Tag, LocalizedString Value)[] variables = null, Sprite icon = null, Color? iconColor = null, Action onDisplay = null, Action onUpdate = null)
{
if (string.IsNullOrEmpty(hintIdentifier)) { return false; }
if (hintIdentifier == Identifier.Empty) { return false; }
if (!HintIdentifiers.Contains(hintIdentifier)) { return false; }
if (GameMain.Config.IgnoredHints.Contains(hintIdentifier)) { return false; }
if (IgnoredHints.Instance.Contains(hintIdentifier)) { return false; }
if (HintsIgnoredThisRound.Contains(hintIdentifier)) { return false; }
string text;
string textTag = extendTextTag ? $"hint.{hintIdentifier}" : hintIdentifier;
if (variableTags != null && variableTags != null && variableTags.Length > 0 && variableTags.Length == variableValues.Length)
LocalizedString text;
Identifier textTag = extendTextTag ? $"hint.{hintIdentifier}".ToIdentifier() : hintIdentifier;
if (variables != null && variables.Length > 0)
{
text = TextManager.GetWithVariables(textTag, variableTags, variableValues, returnNull: true);
text = TextManager.GetWithVariables(textTag, variables);
}
else
{
text = TextManager.Get(textTag, returnNull: true);
text = TextManager.Get(textTag);
}
if (string.IsNullOrEmpty(text))
if (text.IsNullOrEmpty())
{
#if DEBUG
DebugConsole.ThrowError($"No hint text found for text tag \"{textTag}\"");
@@ -668,20 +667,20 @@ namespace Barotrauma
ActiveHintMessageBox.InnerFrame.Flash(color: iconColor ?? Color.Orange, flashDuration: 0.75f);
onDisplay?.Invoke();
GameAnalyticsManager.AddDesignEvent($"HintManager:{GameMain.GameSession?.GameMode?.Preset?.Identifier ?? "none"}:HintDisplayed:{hintIdentifier}");
GameAnalyticsManager.AddDesignEvent($"HintManager:{GameMain.GameSession?.GameMode?.Preset?.Identifier ?? "none".ToIdentifier()}:HintDisplayed:{hintIdentifier}");
return true;
}
public static bool OnDontShowAgain(GUITickBox tickBox)
{
IgnoreHint((string)tickBox.UserData, ignore: tickBox.Selected);
IgnoreHint((Identifier)tickBox.UserData, ignore: tickBox.Selected);
return true;
}
private static void IgnoreHint(string hintIdentifier, bool ignore = true)
private static void IgnoreHint(Identifier hintIdentifier, bool ignore = true)
{
if (string.IsNullOrEmpty(hintIdentifier)) { return; }
if (hintIdentifier.IsEmpty) { return; }
if (!HintIdentifiers.Contains(hintIdentifier))
{
#if DEBUG
@@ -691,29 +690,32 @@ namespace Barotrauma
}
if (ignore)
{
GameMain.Config.IgnoredHints.Add(hintIdentifier);
IgnoredHints.Instance.Add(hintIdentifier);
}
else
{
GameMain.Config.IgnoredHints.Remove(hintIdentifier);
IgnoredHints.Instance.Remove(hintIdentifier);
}
}
private static void IgnoreReminder(string reminderIdentifier)
{
HintsIgnoredThisRound.Add($"reminder.{reminderIdentifier}");
HintsIgnoredThisRound.Add($"reminder.{reminderIdentifier}".ToIdentifier());
}
public static bool OnDisableHints(GUITickBox tickBox)
{
GameMain.Config.DisableInGameHints = tickBox.Selected;
return GameMain.Config.SaveNewPlayerConfig();
var config = GameSettings.CurrentConfig;
config.DisableInGameHints = tickBox.Selected;
GameSettings.SetCurrentConfig(config);
GameSettings.SaveCurrentConfig();
return true;
}
private static bool CanDisplayHints(bool requireGameScreen = true, bool requireControllingCharacter = true)
{
if (HintIdentifiers == null) { return false; }
if (GameMain.Config.DisableInGameHints) { return false; }
if (GameSettings.CurrentConfig.DisableInGameHints) { return false; }
if (ActiveHintMessageBox != null) { return false; }
if (requireControllingCharacter && Character.Controlled == null) { return false; }
var gameMode = GameMain.GameSession?.GameMode;

View File

@@ -181,6 +181,11 @@ namespace Barotrauma
}
}
private void OnMoneyChanged(WalletChangedEvent e)
{
if (e.Wallet.IsOwnWallet) { OnUpdate?.Invoke(); }
}
// if you have more than 5000 ping there are probably more important things to worry about but hey just in case
private static DateTimeOffset GetTimeout() => DateTimeOffset.Now.AddSeconds(5).AddMilliseconds(GetPing());
@@ -241,6 +246,7 @@ namespace Barotrauma
private void HealRequestReceived(IReadMessage inc)
{
NetHealRequest request = INetSerializableStruct.Read<NetHealRequest>(inc);
if (request.Result == HealRequestResult.Success)
{
HealAllPending(force: true);

View File

@@ -9,16 +9,18 @@ namespace Barotrauma
{
internal partial class ReadyCheck
{
private static string readyCheckBody(string name) => string.IsNullOrWhiteSpace(name) ? TextManager.Get("readycheck.serverbody") : TextManager.GetWithVariable("readycheck.body", "[player]", name);
private static LocalizedString readyCheckBody(string name) => string.IsNullOrWhiteSpace(name) ? TextManager.Get("readycheck.serverbody") : TextManager.GetWithVariable("readycheck.body", "[player]", name);
private static string readyCheckStatus(int ready, int total) => TextManager.GetWithVariables("readycheck.readycount", new[] { "[ready]", "[total]" }, new[] { ready.ToString(), total.ToString() });
private static string readyCheckPleaseWait(int seconds) => TextManager.GetWithVariable("readycheck.pleasewait", "[seconds]", seconds.ToString());
private static LocalizedString readyCheckStatus(int ready, int total) => TextManager.GetWithVariables("readycheck.readycount",
("[ready]", ready.ToString()),
("[total]", total.ToString()));
private static LocalizedString readyCheckPleaseWait(int seconds) => TextManager.GetWithVariable("readycheck.pleasewait", "[seconds]", seconds.ToString());
private static readonly string readyCheckHeader = TextManager.Get("ReadyCheck.Title");
private static readonly LocalizedString readyCheckHeader = TextManager.Get("ReadyCheck.Title");
private static readonly string noButton = TextManager.Get("No"),
yesButton = TextManager.Get("Yes"),
closeButton = TextManager.Get("Close");
private static readonly LocalizedString noButton = TextManager.Get("No"),
yesButton = TextManager.Get("Yes"),
closeButton = TextManager.Get("Close");
private const string TimerData = "Timer",
PromptData = "ReadyCheck",
@@ -42,7 +44,7 @@ namespace Barotrauma
msgBox = new GUIMessageBox(readyCheckHeader, readyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true };
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.125f), msgBox.Content.RectTransform), childAnchor: Anchor.Center);
new GUIProgressBar(new RectTransform(new Vector2(0.8f, 1f), contentLayout.RectTransform), time / endTime, GUI.Style.Orange) { UserData = TimerData };
new GUIProgressBar(new RectTransform(new Vector2(0.8f, 1f), contentLayout.RectTransform), time / endTime, GUIStyle.Orange) { UserData = TimerData };
// Yes
msgBox.Buttons[0].OnClicked = delegate
@@ -222,7 +224,7 @@ namespace Barotrauma
int readyCount = Clients.Count(pair => pair.Value == ReadyStatus.Yes);
int totalCount = Clients.Count;
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount), ChatMessageType.Server, null));
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount).Value, ChatMessageType.Server, null));
}
private void UpdateState(byte id, ReadyStatus status)
@@ -256,7 +258,7 @@ namespace Barotrauma
return;
}
image.ApplyStyle(GUI.Style.GetComponentStyle(style));
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
}
}
}

View File

@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
@@ -15,7 +16,6 @@ namespace Barotrauma
private int jobColumnWidth, characterColumnWidth, statusColumnWidth;
private readonly SubmarineInfo sub;
private readonly List<Mission> selectedMissions;
private readonly Location startLocation, endLocation;
@@ -30,11 +30,8 @@ namespace Barotrauma
public GUIComponent Frame { get; private set; }
public RoundSummary(SubmarineInfo sub, GameMode gameMode, IEnumerable<Mission> selectedMissions, Location startLocation, Location endLocation)
public RoundSummary(GameMode gameMode, IEnumerable<Mission> selectedMissions, Location startLocation, Location endLocation)
{
this.sub = sub;
this.gameMode = gameMode;
this.selectedMissions = selectedMissions.ToList();
this.startLocation = startLocation;
@@ -59,7 +56,7 @@ namespace Barotrauma
if (!singleplayer)
{
SoundPlayer.OverrideMusicType = gameOver ? "crewdead" : "endround";
SoundPlayer.OverrideMusicType = (gameOver ? "crewdead" : "endround").ToIdentifier();
SoundPlayer.OverrideMusicDuration = 18.0f;
}
@@ -84,7 +81,7 @@ namespace Barotrauma
};
var crewHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent.RectTransform),
TextManager.Get("crew"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
TextManager.Get("crew"), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
crewHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader.Rect.Height * 2.0f));
CreateCrewList(crewContent, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID != CharacterTeamType.Team2));
@@ -101,19 +98,19 @@ namespace Barotrauma
Stretch = true
};
var crewHeader2 = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent2.RectTransform),
CombatMission.GetTeamName(CharacterTeamType.Team2), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
CombatMission.GetTeamName(CharacterTeamType.Team2), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
crewHeader2.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader2.Rect.Height * 2.0f));
CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == CharacterTeamType.Team2));
}
//header -------------------------------------------------------------------------------
string headerText = GetHeaderText(gameOver, transitionType);
LocalizedString headerText = GetHeaderText(gameOver, transitionType);
GUITextBlock headerTextBlock = null;
if (!string.IsNullOrEmpty(headerText))
if (!headerText.IsNullOrEmpty())
{
headerTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), crewFrame.RectTransform, Anchor.TopLeft, Pivot.BottomLeft),
headerText, textAlignment: Alignment.BottomLeft, font: GUI.LargeFont, wrap: true);
headerText, textAlignment: Alignment.BottomLeft, font: GUIStyle.LargeFont, wrap: true);
}
//traitor panel -------------------------------------------------------------------------------
@@ -130,14 +127,14 @@ namespace Barotrauma
};
var traitorHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), traitorContent.RectTransform),
TextManager.Get("traitors"), font: GUI.SubHeadingFont);
TextManager.Get("traitors"), font: GUIStyle.SubHeadingFont);
traitorHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(traitorHeader.Rect.Height * 2.0f));
GUIListBox listBox = CreateCrewList(traitorContent, traitorResults.SelectMany(tr => tr.Characters.Select(c => c.Info)));
foreach (var traitorResult in traitorResults)
{
var traitorMission = TraitorMissionPrefab.List.Find(t => t.Identifier == traitorResult.MissionIdentifier);
var traitorMission = TraitorMissionPrefab.Prefabs.Find(t => t.Identifier == traitorResult.MissionIdentifier);
if (traitorMission == null) { continue; }
//spacing
@@ -154,8 +151,8 @@ namespace Barotrauma
Color = traitorMission.IconColor
};
string traitorMessage = TextManager.GetServerMessage(traitorResult.EndMessage);
if (!string.IsNullOrEmpty(traitorMessage))
LocalizedString traitorMessage = TextManager.GetServerMessage(traitorResult.EndMessage);
if (!traitorMessage.IsNullOrEmpty())
{
var textContent = new GUILayoutGroup(new RectTransform(Vector2.One, traitorResultHorizontal.RectTransform))
{
@@ -164,10 +161,10 @@ namespace Barotrauma
var traitorStatusText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
TextManager.Get(traitorResult.Success ? "missioncompleted" : "missionfailed"),
textColor: traitorResult.Success ? GUI.Style.Green : GUI.Style.Red, font: GUI.SubHeadingFont);
textColor: traitorResult.Success ? GUIStyle.Green : GUIStyle.Red, font: GUIStyle.SubHeadingFont);
var traitorMissionInfo = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
traitorMessage, font: GUI.SmallFont, wrap: true);
traitorMessage, font: GUIStyle.SmallFont, wrap: true);
traitorResultHorizontal.Recalculate();
@@ -196,7 +193,7 @@ namespace Barotrauma
};
var reputationHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), reputationContent.RectTransform),
TextManager.Get("reputation"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
TextManager.Get("reputation"), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
reputationHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(reputationHeader.Rect.Height * 2.0f));
CreateReputationInfoPanel(reputationContent, campaignMode);
@@ -233,7 +230,7 @@ namespace Barotrauma
if (missionsToDisplay.Any())
{
var missionHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContent.RectTransform),
TextManager.Get(missionsToDisplay.Count > 1 ? "Missions" : "Mission"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
TextManager.Get(missionsToDisplay.Count > 1 ? "Missions" : "Mission"), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
missionHeader.RectTransform.MinSize = new Point(0, (int)(missionHeader.Rect.Height * 1.2f));
}
@@ -271,7 +268,7 @@ namespace Barotrauma
Stretch = true
};
string missionMessage =
LocalizedString missionMessage =
selectedMissions.Contains(displayedMission) ?
displayedMission.Completed ? displayedMission.SuccessMessage : displayedMission.FailureMessage :
displayedMission.Description;
@@ -293,7 +290,7 @@ namespace Barotrauma
};
missionContentHorizontal.Recalculate();
var missionNameTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
displayedMission.Name, font: GUI.SubHeadingFont);
displayedMission.Name, font: GUIStyle.SubHeadingFont);
if (displayedMission.Difficulty.HasValue)
{
var groupSize = missionNameTextBlock.Rect.Size;
@@ -314,12 +311,21 @@ namespace Barotrauma
}
}
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
missionMessage, wrap: true, parseRichText: true);
RichString.Rich(missionMessage), wrap: true);
int reward = displayedMission.GetReward(Submarine.MainSub);
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && reward > 0)
{
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", reward));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), displayedMission.GetMissionRewardText(Submarine.MainSub), parseRichText: true);
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)
{
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(reward));
if (share > 0)
{
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}")));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString);
}
}
}
if (displayedMission != missionsToDisplay.Last())
@@ -346,7 +352,7 @@ namespace Barotrauma
GUIImage missionIcon = new GUIImage(new RectTransform(new Point((int)(missionContentHorizontal.Rect.Height * 0.7f)), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
missionIcon.RectTransform.MinSize = new Point((int)(missionContentHorizontal.Rect.Height * 0.7f));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContentHorizontal.RectTransform),
TextManager.Get("nomission"), font: GUI.LargeFont);
TextManager.Get("nomission"), font: GUIStyle.LargeFont);
}
/*missionContentHorizontal.Recalculate();
@@ -397,7 +403,7 @@ namespace Barotrauma
if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
{
var iconStyle = GUI.Style.GetComponentStyle("LocationReputationIcon");
var iconStyle = GUIStyle.GetComponentStyle("LocationReputationIcon");
var locationFrame = CreateReputationElement(
reputationList.Content,
startLocation.Name,
@@ -407,7 +413,7 @@ namespace Barotrauma
CreatePathUnlockElement(locationFrame, null, startLocation);
}
foreach (Faction faction in campaignMode.Factions)
foreach (Faction faction in campaignMode.Factions.OrderBy(f => f.Prefab.MenuOrder).ThenBy(f => f.Prefab.Name))
{
float initialReputation = faction.Reputation.Value;
if (initialFactionReputations.ContainsKey(faction))
@@ -431,7 +437,12 @@ namespace Barotrauma
foreach (GUIComponent child in reputationList.Content.Children)
{
var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
maxDescriptionHeight = Math.Max(maxDescriptionHeight, descriptionElement.TextSize.Y * 1.1f);
float descriptionHeight = descriptionElement.TextSize.Y * 1.1f;
if (child.FindChild("unlockinfo") is GUIComponent unlockInfoComponent)
{
descriptionHeight += 1.25f * unlockInfoComponent.Rect.Height;
}
maxDescriptionHeight = Math.Max(maxDescriptionHeight, descriptionHeight);
}
foreach (GUIComponent child in reputationList.Content.Children)
{
@@ -452,8 +463,8 @@ namespace Barotrauma
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
var unlockEvent =
EventSet.PrefabList.Find(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == gateLocation.LevelData.Biome.Identifier) ??
EventSet.PrefabList.Find(ep => ep.UnlockPathEvent && string.IsNullOrEmpty(ep.BiomeIdentifier));
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 (string.IsNullOrEmpty(unlockEvent.UnlockPathFaction) || unlockEvent.UnlockPathFaction.Equals("location", StringComparison.OrdinalIgnoreCase))
@@ -462,7 +473,7 @@ namespace Barotrauma
}
else
{
if (faction == null || !faction.Prefab.Identifier.Equals(unlockEvent.UnlockPathFaction, StringComparison.OrdinalIgnoreCase)) { continue; }
if (faction == null || faction.Prefab.Identifier != unlockEvent.UnlockPathFaction) { continue; }
}
if (unlockEvent != null)
@@ -471,20 +482,21 @@ namespace Barotrauma
Faction unlockFaction = null;
if (!string.IsNullOrEmpty(unlockEvent.UnlockPathFaction))
{
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier.Equals(unlockEvent.UnlockPathFaction, StringComparison.OrdinalIgnoreCase));
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);
string unlockText = TextManager.GetWithVariables(
RichString unlockText = RichString.Rich(TextManager.GetWithVariables(
"lockedpathreputationrequirement",
new string[] { "[reputation]", "[biomename]" },
new string[] { Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true), $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖" });
("[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: GUI.Style.TextColor, parseRichText: true);
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 = GUI.SmallFont;
unlockInfoPanel.Font = GUIStyle.SmallFont;
}
}
}
@@ -492,7 +504,7 @@ namespace Barotrauma
}
}
private string GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
private LocalizedString GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
{
string locationName = Submarine.MainSub.AtEndExit ? endLocation?.Name : startLocation?.Name;
@@ -539,14 +551,14 @@ namespace Barotrauma
locationName = "[UNKNOWN]";
}
string subName = string.Empty;
LocalizedString subName = string.Empty;
SubmarineInfo currentOrPending = SubmarineSelection.CurrentOrPendingSubmarine();
if (currentOrPending != null)
{
subName = currentOrPending.DisplayName;
}
return TextManager.GetWithVariables(textTag, new string[2] { "[sub]", "[location]" }, new string[2] { subName, locationName });
return TextManager.GetWithVariables(textTag, ("[sub]", subName), ("[location]", locationName));
}
private GUIListBox CreateCrewList(GUIComponent parent, IEnumerable<CharacterInfo> characterInfos)
@@ -566,9 +578,9 @@ namespace Barotrauma
characterButton.RectTransform.RelativeSize = new Vector2(characterColumnWidthPercentage * sizeMultiplier, 1f);
statusButton.RectTransform.RelativeSize = new Vector2(statusColumnWidthPercentage * sizeMultiplier, 1f);
jobButton.TextBlock.Font = characterButton.TextBlock.Font = statusButton.TextBlock.Font = GUI.HotkeyFont;
jobButton.TextBlock.Font = characterButton.TextBlock.Font = statusButton.TextBlock.Font = GUIStyle.HotkeyFont;
jobButton.CanBeFocused = characterButton.CanBeFocused = statusButton.CanBeFocused = false;
jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = statusButton.ForceUpperCase = true;
jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = statusButton.ForceUpperCase = ForceUpperCase.Yes;
jobColumnWidth = jobButton.Rect.Width;
characterColumnWidth = characterButton.Rect.Width;
@@ -615,10 +627,10 @@ namespace Barotrauma
};
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
ToolBox.LimitString(characterInfo.Name, GUI.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: characterInfo.Job.Prefab.UIColor);
ToolBox.LimitString(characterInfo.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: characterInfo.Job.Prefab.UIColor);
string statusText = TextManager.Get("StatusOK");
Color statusColor = GUI.Style.Green;
LocalizedString statusText = TextManager.Get("StatusOK");
Color statusColor = GUIStyle.Green;
Character character = characterInfo.Character;
if (character == null || character.IsDead)
@@ -626,7 +638,7 @@ namespace Barotrauma
if (character == null && characterInfo.IsNewHire && characterInfo.CauseOfDeath == null)
{
statusText = TextManager.Get("CampaignCrew.NewHire");
statusColor = GUI.Style.Blue;
statusColor = GUIStyle.Blue;
}
else if (characterInfo.CauseOfDeath == null)
{
@@ -637,9 +649,9 @@ namespace Barotrauma
{
string errorMsg = "Character \"[name]\" had an invalid cause of death (the type of the cause of death was Affliction, but affliction was not specified).";
DebugConsole.ThrowError(errorMsg.Replace("[name]", characterInfo.Name));
GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", characterInfo.SpeciesName));
GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", characterInfo.SpeciesName.Value));
statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
statusColor = GUI.Style.Red;
statusColor = GUIStyle.Red;
}
else
{
@@ -664,12 +676,12 @@ namespace Barotrauma
}
GUITextBlock statusBlock = new GUITextBlock(new RectTransform(new Point(statusColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
ToolBox.LimitString(statusText, GUI.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: statusColor);
ToolBox.LimitString(statusText.Value, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: statusColor);
}
private GUIFrame CreateReputationElement(GUIComponent parent,
string name, float reputation, float normalizedReputation, float initialReputation,
string shortDescription, string fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor)
LocalizedString name, float reputation, float normalizedReputation, float initialReputation,
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);
@@ -697,14 +709,14 @@ namespace Barotrauma
AbsoluteSpacing = GUI.IntScale(10),
Stretch = true
};
var factionIcon = new GUIImage(new RectTransform(new Point((int)(factionInfoHorizontal.Rect.Height * 0.7f)), factionInfoHorizontal.RectTransform, scaleBasis: ScaleBasis.Smallest), icon, scaleToFit: true)
var factionIcon = new GUIImage(new RectTransform(Vector2.One * 0.7f, factionInfoHorizontal.RectTransform, scaleBasis: ScaleBasis.Smallest), icon, scaleToFit: true)
{
Color = iconColor
};
factionInfoHorizontal.Recalculate();
var header = new GUITextBlock(new RectTransform(new Point(factionTextContent.Rect.Width, GUI.IntScale(40)), factionTextContent.RectTransform),
name, font: GUI.SubHeadingFont)
name, font: GUIStyle.SubHeadingFont)
{
Padding = Vector4.Zero,
UserData = "header"
@@ -723,34 +735,34 @@ namespace Barotrauma
new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform),
onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, normalizedReputation));
string reputationText = Reputation.GetFormattedReputationText(normalizedReputation, reputation, addColorTags: true);
LocalizedString reputationText = Reputation.GetFormattedReputationText(normalizedReputation, reputation, addColorTags: true);
int reputationChange = (int)Math.Round(reputation - initialReputation);
if (Math.Abs(reputationChange) > 0)
{
string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}";
string colorStr = XMLExtensions.ColorToString(reputationChange > 0 ? GUI.Style.Green : GUI.Style.Red);
var rtData = RichTextData.GetRichTextData($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)", out string sanitizedText);
string colorStr = XMLExtensions.ToStringHex(reputationChange > 0 ? GUIStyle.Green : GUIStyle.Red);
var richText = RichString.Rich($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)");
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
rtData, sanitizedText,
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont);
richText,
textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont);
}
else
{
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
reputationText,
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont, parseRichText: true);
RichString.Rich(reputationText),
textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont);
}
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), factionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(5)) }, style: null);
var factionDescription = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.6f), factionTextContent.RectTransform),
shortDescription, font: GUI.SmallFont, wrap: true)
shortDescription, font: GUIStyle.SmallFont, wrap: true)
{
UserData = "description",
Padding = Vector4.Zero
};
if (shortDescription != fullDescription && !string.IsNullOrEmpty(fullDescription))
if (shortDescription != fullDescription && !fullDescription.IsNullOrEmpty())
{
factionDescription.ToolTip = fullDescription;
}
@@ -771,16 +783,16 @@ namespace Barotrauma
for (int i = 0; i < 5; i++)
{
GUI.DrawRectangle(sb, new Rectangle(rect.X + (segmentWidth * i), rect.Y, segmentWidth, rect.Height), Reputation.GetReputationColor(i / 5.0f), isFilled: true);
GUI.DrawRectangle(sb, new Rectangle(rect.X + (segmentWidth * i), rect.Y, segmentWidth, rect.Height), GUI.Style.ColorInventoryBackground, isFilled: false);
GUI.DrawRectangle(sb, new Rectangle(rect.X + (segmentWidth * i), rect.Y, segmentWidth, rect.Height), GUIStyle.ColorInventoryBackground, isFilled: false);
}
GUI.DrawRectangle(sb, rect, GUI.Style.ColorInventoryBackground, isFilled: false);
GUI.DrawRectangle(sb, rect, GUIStyle.ColorInventoryBackground, isFilled: false);
GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUI.Style.ColorInventoryBackground, scale: GUI.Scale, spriteEffect: SpriteEffects.FlipVertically);
GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUI.Style.TextColor, scale: GUI.Scale * 0.8f, spriteEffect: SpriteEffects.FlipVertically);
GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUIStyle.ColorInventoryBackground, scale: GUI.Scale, spriteEffect: SpriteEffects.FlipVertically);
GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUIStyle.TextColorNormal, scale: GUI.Scale * 0.8f, spriteEffect: SpriteEffects.FlipVertically);
GUI.DrawString(sb, new Vector2(rect.X, rect.Bottom), "-100", GUI.Style.TextColor, font: GUI.SmallFont);
Vector2 textSize = GUI.SmallFont.MeasureString("100");
GUI.DrawString(sb, new Vector2(rect.Right - textSize.X, rect.Bottom), "100", GUI.Style.TextColor, font: GUI.SmallFont);
GUI.DrawString(sb, new Vector2(rect.X, rect.Bottom), "-100", GUIStyle.TextColorNormal, font: GUIStyle.SmallFont);
Vector2 textSize = GUIStyle.SmallFont.MeasureString("100");
GUI.DrawString(sb, new Vector2(rect.Right - textSize.X, rect.Bottom), "100", GUIStyle.TextColorNormal, font: GUIStyle.SmallFont);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -109,7 +109,7 @@ namespace Barotrauma
indicatorGroup = new GUILayoutGroup(new RectTransform(Point.Zero, hideButton.RectTransform)) { IsHorizontal = false };
indicatorGroup.ChildAnchor = Anchor.TopCenter;
indicatorSpriteSize = GUI.Style.GetComponentStyle("EquipmentIndicatorDivingSuit").GetDefaultSprite().size;
indicatorSpriteSize = GUIStyle.GetComponentStyle("EquipmentIndicatorDivingSuit").GetDefaultSprite().size;
indicators[0] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorDivingSuit");
indicators[1] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorID");
@@ -522,7 +522,7 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam, bool isSubInventory = false)
{
if (!AccessibleWhenAlive && !character.IsDead)
if (!AccessibleWhenAlive && !character.IsDead && !AccessibleByOwner)
{
syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f);
return;
@@ -814,21 +814,21 @@ namespace Barotrauma
if (conditionPercentage != -1)
{
indicators[i].Color = ToolBox.GradientLerp(conditionPercentage, GUI.Style.EquipmentIndicatorRunningOut, GUI.Style.EquipmentIndicatorEquipped);
indicators[i].Color = ToolBox.GradientLerp(conditionPercentage, GUIStyle.EquipmentIndicatorRunningOut, GUIStyle.EquipmentIndicatorEquipped);
}
else
{
indicators[i].Color = GUI.Style.EquipmentIndicatorRunningOut;
indicators[i].Color = GUIStyle.EquipmentIndicatorRunningOut;
}
}
else
{
indicators[i].Color = GUI.Style.EquipmentIndicatorEquipped;
indicators[i].Color = GUIStyle.EquipmentIndicatorEquipped;
}
}
else
{
indicators[i].Color = GUI.Style.EquipmentIndicatorNotEquipped;
indicators[i].Color = GUIStyle.EquipmentIndicatorNotEquipped;
}
}
}
@@ -966,7 +966,7 @@ namespace Barotrauma
}
else if (character.HeldItems.Any(i =>
i.OwnInventory != null &&
(i.OwnInventory.CanBePut(item) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
((i.OwnInventory.CanBePut(item) && allowInventorySwap) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
{
return QuickUseAction.PutToEquippedItem;
}
@@ -1007,7 +1007,7 @@ namespace Barotrauma
var slot = invSlots[i];
if (item.ParentInventory.GetItemAt(i) == item)
{
slot.ShowBorderHighlight(GUI.Style.Red, 0.1f, 0.4f);
slot.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.4f);
SoundPlayer.PlayUISound(GUISoundType.PickItem);
break;
}
@@ -1033,7 +1033,7 @@ namespace Barotrauma
{
if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "equipconfirmation")) { return; }
var equipConfirmation = new GUIMessageBox(string.Empty, TextManager.Get(item.Prefab.EquipConfirmationText),
new string[] { TextManager.Get("yes"), TextManager.Get("no") })
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") })
{
UserData = "equipconfirmation"
};
@@ -1138,7 +1138,7 @@ namespace Barotrauma
success = true;
for (int j = 0; j < capacity; j++)
{
if (slots[j].Contains(heldItem)) { visualSlots[j].ShowBorderHighlight(GUI.Style.Green, 0.1f, 0.4f); }
if (slots[j].Contains(heldItem)) { visualSlots[j].ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.4f); }
}
break;
}
@@ -1150,7 +1150,7 @@ namespace Barotrauma
{
for (int i = 0; i < capacity; i++)
{
if (slots[i].Contains(item)) { visualSlots[i].ShowBorderHighlight(GUI.Style.Green, 0.1f, 0.4f); }
if (slots[i].Contains(item)) { visualSlots[i].ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.4f); }
}
}
@@ -1163,7 +1163,7 @@ namespace Barotrauma
public void DrawOwn(SpriteBatch spriteBatch)
{
if (!AccessibleWhenAlive && !character.IsDead) { return; }
if (!AccessibleWhenAlive && !character.IsDead && !AccessibleByOwner) { return; }
if (capacity == 0) { return; }
if (visualSlots == null) { CreateSlots(); }
if (GameMain.GraphicsWidth != screenResolution.X ||
@@ -1182,7 +1182,7 @@ namespace Barotrauma
CalculateBackgroundFrame();
GUI.DrawRectangle(spriteBatch, BackgroundFrame, Color.Black * 0.8f, true);
GUI.DrawString(spriteBatch,
new Vector2((int)(BackgroundFrame.Center.X - GUI.Font.MeasureString(character.Name).X / 2), (int)BackgroundFrame.Y + 5),
new Vector2((int)(BackgroundFrame.Center.X - GUIStyle.Font.MeasureString(character.Name).X / 2), (int)BackgroundFrame.Y + 5),
character.Name, Color.White * 0.9f);
}
@@ -1218,7 +1218,7 @@ namespace Barotrauma
if (LimbSlotIcons.ContainsKey(SlotTypes[i]))
{
var icon = LimbSlotIcons[SlotTypes[i]];
icon.Draw(spriteBatch, visualSlots[i].Rect.Center.ToVector2() + visualSlots[i].DrawOffset, GUI.Style.EquipmentSlotIconColor, origin: icon.size / 2, scale: visualSlots[i].Rect.Width / icon.size.X);
icon.Draw(spriteBatch, visualSlots[i].Rect.Center.ToVector2() + visualSlots[i].DrawOffset, GUIStyle.EquipmentSlotIconColor, origin: icon.size / 2, scale: visualSlots[i].Rect.Width / icon.size.X);
}
continue;
}
@@ -1292,14 +1292,14 @@ namespace Barotrauma
if (Locked)
{
GUI.DrawRectangle(spriteBatch, inventoryArea, new Color(30,30,30,100), isFilled: true);
var lockIcon = GUI.Style.GetComponentStyle("LockIcon")?.GetDefaultSprite();
var lockIcon = GUIStyle.GetComponentStyle("LockIcon")?.GetDefaultSprite();
lockIcon?.Draw(spriteBatch, inventoryArea.Center.ToVector2(), scale: Math.Min(inventoryArea.Height / lockIcon.size.Y * 0.7f, 1.0f));
if (inventoryArea.Contains(PlayerInput.MousePosition))
{
GUIComponent.DrawToolTip(spriteBatch, TextManager.Get("handcuffed"), new Rectangle(inventoryArea.Center - new Point(inventoryArea.Height / 2), new Point(inventoryArea.Height)));
}
}
else if (highlightedQuickUseSlot != null && !string.IsNullOrEmpty(highlightedQuickUseSlot.QuickUseButtonToolTip))
else if (highlightedQuickUseSlot != null && !highlightedQuickUseSlot.QuickUseButtonToolTip.IsNullOrEmpty())
{
GUIComponent.DrawToolTip(spriteBatch, highlightedQuickUseSlot.QuickUseButtonToolTip, highlightedQuickUseSlot.EquipButtonRect);
}

View File

@@ -19,7 +19,7 @@ namespace Barotrauma.Items.Components
//openState when the vertices of the convex hull were last calculated
private float lastConvexHullState;
[Serialize("1,1", false, description: "The scale of the shadow-casting area of the door (relative to the actual size of the door).")]
[Serialize("1,1", IsPropertySaveable.No, description: "The scale of the shadow-casting area of the door (relative to the actual size of the door).")]
public Vector2 ShadowScale
{
get;
@@ -283,9 +283,9 @@ namespace Barotrauma.Items.Components
}
}
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
public override void ClientEventRead(IReadMessage msg, float sendingTime)
{
base.ClientRead(type, msg, sendingTime);
base.ClientEventRead(msg, sendingTime);
bool open = msg.ReadBoolean();
bool broken = msg.ReadBoolean();

View File

@@ -54,7 +54,7 @@ namespace Barotrauma.Items.Components
}
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
CurrPowerConsumption = powerConsumption;
charging = true;

View File

@@ -17,12 +17,12 @@ namespace Barotrauma.Items.Components
case AreaShape.Rectangle:
{
RectangleF rect = GetAreaRectangle(SpawnAreaBounds, SpawnAreaOffset, draw: true);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Red, isFilled: false, 0f, 4f);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUIStyle.Red, isFilled: false, 0f, 4f);
if (MaximumAmountRangePadding > 0f)
{
rect.Inflate(MaximumAmountRangePadding, MaximumAmountRangePadding);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Red, isFilled: false, 0f, 2f);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUIStyle.Red, isFilled: false, 0f, 2f);
}
break;
}
@@ -30,11 +30,11 @@ namespace Barotrauma.Items.Components
Vector2 center = item.WorldPosition;
center += SpawnAreaOffset;
center.Y = -center.Y;
spriteBatch.DrawCircle(center, SpawnAreaRadius, 32, GUI.Style.Red, thickness: 4f);
spriteBatch.DrawCircle(center, SpawnAreaRadius, 32, GUIStyle.Red, thickness: 4f);
if (MaximumAmountRangePadding > 0f)
{
spriteBatch.DrawCircle(center, SpawnAreaRadius + MaximumAmountRangePadding, 32, GUI.Style.Red, thickness: 2f);
spriteBatch.DrawCircle(center, SpawnAreaRadius + MaximumAmountRangePadding, 32, GUIStyle.Red, thickness: 2f);
}
break;
}
@@ -46,14 +46,14 @@ namespace Barotrauma.Items.Components
case AreaShape.Rectangle:
{
RectangleF rect = GetAreaRectangle(CrewAreaBounds, CrewAreaOffset, draw: true);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Green, isFilled: false, 0f, 4f);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUIStyle.Green, isFilled: false, 0f, 4f);
break;
}
case AreaShape.Circle:
Vector2 center = item.WorldPosition;
center += CrewAreaOffset;
center.Y = -center.Y;
spriteBatch.DrawCircle(center, CrewAreaRadius, 32, GUI.Style.Green);
spriteBatch.DrawCircle(center, CrewAreaRadius, 32, GUIStyle.Green);
break;
}
}

View File

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

View File

@@ -1,6 +1,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
@@ -10,15 +11,15 @@ namespace Barotrauma.Items.Components
{
internal class VineSprite
{
[Serialize("0,0,0,0", false)]
[Serialize("0,0,0,0", IsPropertySaveable.No)]
public Rectangle SourceRect { get; private set; }
[Serialize("0.5,0.5", false)]
[Serialize("0.5,0.5", IsPropertySaveable.No)]
public Vector2 Origin { get; private set; }
public Vector2 AbsoluteOrigin;
public VineSprite(XElement element)
public VineSprite(ContentXElement element)
{
SerializableProperty.DeserializeProperties(this, element);
AbsoluteOrigin = new Vector2(SourceRect.Width * Origin.X, SourceRect.Height * Origin.Y);
@@ -109,28 +110,27 @@ namespace Barotrauma.Items.Components
}
}
partial void LoadVines(XElement element)
partial void LoadVines(ContentXElement element)
{
string? vineAtlasPath = element.GetAttributeString("vineatlas", null);
string? decayAtlasPath = element.GetAttributeString("decayatlas", null);
ContentPath vineAtlasPath = element.GetAttributeContentPath("vineatlas") ?? ContentPath.Empty;
ContentPath decayAtlasPath = element.GetAttributeContentPath("decayatlas") ?? ContentPath.Empty;
if (vineAtlasPath != null)
if (!vineAtlasPath.IsNullOrEmpty())
{
VineAtlas = new Sprite(vineAtlasPath, Rectangle.Empty);
VineAtlas = new Sprite(vineAtlasPath.Value, Rectangle.Empty);
}
if (decayAtlasPath != null)
if (!decayAtlasPath.IsNullOrEmpty())
{
DecayAtlas = new Sprite(decayAtlasPath, Rectangle.Empty);
DecayAtlas = new Sprite(decayAtlasPath.Value, Rectangle.Empty);
}
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "vinesprite":
var tileType = subElement.GetAttributeString("type", null);
VineTileType type = Enum.Parse<VineTileType>(tileType);
VineTileType type = subElement.GetAttributeEnum("type", VineTileType.Stem);
VineSprites.Add(type, new VineSprite(subElement));
break;
case "flowersprite":
@@ -145,18 +145,18 @@ namespace Barotrauma.Items.Components
leafVariants = LeafSprites.Count;
}
foreach (VineTileType type in Enum.GetValues(typeof(VineTileType)))
foreach (VineTileType type in Enum.GetValues(typeof(VineTileType)).Cast<VineTileType>())
{
if (!VineSprites.ContainsKey(type))
{
DebugConsole.ThrowError($"Vine sprite missing from {item.prefab.Identifier}: {type}");
DebugConsole.ThrowError($"Vine sprite missing from {item.Prefab.Identifier}: {type}");
}
}
}
private readonly object mutex = new object();
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
Health = msg.ReadRangedSingle(0, MaxHealth, 8);
int startOffset = msg.ReadRangedInteger(-1, MaximumVines);

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