Merge branch 'dev' of https://github.com/Regalis11/Barotrauma into unstable

This commit is contained in:
EvilFactory
2022-09-29 12:13:55 -03:00
602 changed files with 19759 additions and 16312 deletions

View File

@@ -52,7 +52,8 @@ body:
label: Version
description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu.
options:
- 0.18.15.0
- 0.18.15.1 / 0.18.15.2 on macOS
- 0.19.8.0 (unstable)
- Other
validations:
required: true

View File

@@ -6,7 +6,7 @@ using System;
namespace Barotrauma
{
public class Camera : IDisposable
class Camera : IDisposable
{
public static bool FollowSub = true;
@@ -179,9 +179,9 @@ namespace Barotrauma
{
if (Character.Controlled != null && !Character.Controlled.IsDead) { return; }
msg.Write((byte)ClientNetObject.SPECTATING_POS);
msg.Write(position.X);
msg.Write(position.Y);
msg.WriteByte((byte)ClientNetObject.SPECTATING_POS);
msg.WriteSingle(position.X);
msg.WriteSingle(position.Y);
}
private void CreateMatrices()

View File

@@ -159,7 +159,7 @@ namespace Barotrauma
GameMain.LightManager.LosEnabled = true;
}
#endif
timer += CoroutineManager.UnscaledDeltaTime;
timer += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}

View File

@@ -73,6 +73,11 @@ namespace Barotrauma
}
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black);
if (State == AIState.Attack && selectedTargetingParams != null && selectedTargetingParams.AttackPattern == AttackPattern.Circle)
{
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 100.0f, CirclePhase.ToString(), stateColor, Color.Black);
}
if (LatchOntoAI != null && (State == AIState.Idle || LatchOntoAI.IsAttachedToSub))
{
foreach (Joint attachJoint in LatchOntoAI.AttachJoints)

View File

@@ -1,15 +1,14 @@
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Particles;
using Barotrauma.SpriteDeformations;
using Barotrauma.Extensions;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
using System.Collections.Generic;
using Barotrauma.Particles;
using System.Linq;
namespace Barotrauma
{
@@ -55,21 +54,34 @@ namespace Barotrauma
if (character.MemState[0].SelectedItem == null || character.MemState[0].SelectedItem.Removed)
{
character.SelectedConstruction = null;
character.SelectedItem = null;
}
else
else if (character.SelectedItem != character.MemState[0].SelectedItem)
{
if (character.SelectedConstruction != character.MemState[0].SelectedItem)
foreach (var ic in character.MemState[0].SelectedItem.Components)
{
foreach (var ic in character.MemState[0].SelectedItem.Components)
if (ic.CanBeSelected)
{
if (ic.CanBeSelected)
{
ic.Select(character);
}
ic.Select(character);
}
}
character.SelectedConstruction = character.MemState[0].SelectedItem;
character.SelectedItem = character.MemState[0].SelectedItem;
}
if (character.MemState[0].SelectedSecondaryItem == null || character.MemState[0].SelectedSecondaryItem.Removed)
{
character.SelectedSecondaryItem = null;
}
else if (character.SelectedSecondaryItem != character.MemState[0].SelectedSecondaryItem)
{
foreach (var ic in character.MemState[0].SelectedSecondaryItem.Components)
{
if (ic.CanBeSelected)
{
ic.Select(character);
}
}
character.SelectedSecondaryItem = character.MemState[0].SelectedSecondaryItem;
}
if (character.MemState[0].Animation == AnimController.Animation.CPR)
@@ -201,15 +213,24 @@ namespace Barotrauma
{
if (serverPos.SelectedItem == null || serverPos.SelectedItem.Removed)
{
character.SelectedConstruction = null;
character.SelectedItem = null;
}
else if (serverPos.SelectedItem != null)
else if (character.SelectedItem != serverPos.SelectedItem)
{
if (character.SelectedConstruction != serverPos.SelectedItem)
{
serverPos.SelectedItem.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true);
}
character.SelectedConstruction = serverPos.SelectedItem;
serverPos.SelectedItem.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true);
character.SelectedItem = serverPos.SelectedItem;
}
}
if (localPos.SelectedSecondaryItem != serverPos.SelectedSecondaryItem)
{
if (serverPos.SelectedSecondaryItem == null || serverPos.SelectedSecondaryItem.Removed)
{
character.SelectedSecondaryItem = null;
}
else if (character.SelectedSecondaryItem != serverPos.SelectedSecondaryItem)
{
serverPos.SelectedSecondaryItem.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true);
character.SelectedSecondaryItem = serverPos.SelectedSecondaryItem;
}
}
@@ -496,12 +517,11 @@ namespace Barotrauma
float maxDepth = 0.0f;
float minDepth = 1.0f;
float depthOffset = 0.0f;
var ladder = character.SelectedConstruction?.GetComponent<Ladder>();
if (ladder != null)
if (character.SelectedSecondaryItem?.GetComponent<Ladder>() is Ladder ladder)
{
CalculateLimbDepths();
if (character.WorldPosition.X < character.SelectedConstruction.WorldPosition.X)
if (character.WorldPosition.X < character.SelectedSecondaryItem.WorldPosition.X)
{
//at the left side of the ladder, needs to be drawn in front of the rungs
if (maxDepth > ladder.BackgroundSpriteDepth)
@@ -522,16 +542,21 @@ namespace Barotrauma
else
{
CalculateLimbDepths();
var controller = character.SelectedConstruction?.GetComponent<Controller>();
if (controller != null && controller.ControlCharacterPose && controller.User == character && controller.UserInCorrectPosition)
AdjustDepthOffset(character.SelectedItem);
AdjustDepthOffset(character.SelectedSecondaryItem);
void AdjustDepthOffset(Item item)
{
if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind)
if (item?.GetComponent<Controller>() is { ControlCharacterPose: true, UserInCorrectPosition: true } controller && controller.User == character)
{
depthOffset = Math.Max(controller.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
}
else
{
depthOffset = Math.Max(controller.Item.GetDrawDepth() - 0.0001f - maxDepth, 0.0f);
if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind)
{
depthOffset = Math.Max(controller.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
}
else
{
depthOffset = Math.Max(controller.Item.GetDrawDepth() - 0.0001f - maxDepth, 0.0f);
}
}
}
}

View File

@@ -9,7 +9,6 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
@@ -323,8 +322,8 @@ namespace Barotrauma
{
cam.OffsetAmount = targetOffsetAmount = item.Prefab.OffsetOnSelected * item.OffsetOnSelectedMultiplier;
}
else if (SelectedConstruction != null && ViewTarget == null &&
SelectedConstruction.Components.Any(ic => ic?.GuiFrame != null && ic.ShouldDrawHUD(this)))
else if (SelectedItem != null && ViewTarget == null &&
SelectedItem.Components.Any(ic => ic?.GuiFrame != null && ic.ShouldDrawHUD(this)))
{
cam.OffsetAmount = targetOffsetAmount = 0.0f;
cursorPosition =
@@ -368,21 +367,20 @@ namespace Barotrauma
if (!GUI.InputBlockingMenuOpen)
{
if (SelectedConstruction != null &&
(SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) ||
if (SelectedItem != null &&
(SelectedItem.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) ||
((ViewTarget as Item)?.Prefab.FocusOnSelected ?? false) && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)))
{
if (GameMain.Client != null)
{
//emulate a Select input to get the character to deselect the item server-side
//keys[(int)InputType.Select].Hit = true;
keys[(int)InputType.Deselect].Hit = true;
//emulate a Deselect input to get the character to deselect the item server-side
EmulateInput(InputType.Deselect);
}
//reset focus to prevent us from accidentally interacting with another entity
focusedItem = null;
FocusedCharacter = null;
findFocusedTimer = 0.2f;
SelectedConstruction = null;
SelectedItem = null;
}
}
@@ -425,6 +423,11 @@ namespace Barotrauma
}
}
}
public void EmulateInput(InputType input)
{
keys[(int)input].Hit = true;
}
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun)
{
@@ -518,7 +521,7 @@ namespace Barotrauma
//reduce the amount of aim assist if an item has been selected
//= can't switch selection to another item without deselecting the current one first UNLESS the cursor is directly on the item
//otherwise it would be too easy to accidentally switch the selected item when rewiring items
float aimAssistAmount = SelectedConstruction == null ? 100.0f * aimAssistModifier : 1.0f;
float aimAssistAmount = SelectedItem == null ? 100.0f * aimAssistModifier : 1.0f;
Vector2 displayPosition = ConvertUnits.ToDisplayUnits(simPosition);
@@ -623,12 +626,12 @@ namespace Barotrauma
{
if (this != controlled) { return false; }
if (GameMain.GameSession?.Campaign != null && GameMain.GameSession.Campaign.ShowCampaignUI) { return true; }
var controller = SelectedConstruction?.GetComponent<Controller>();
var controller = SelectedItem?.GetComponent<Controller>();
//lock if using a controller, except if we're also using a connection panel in the same item
return
SelectedConstruction != null &&
SelectedItem != null &&
controller?.User == this && controller.HideHUD &&
SelectedConstruction?.GetComponent<ConnectionPanel>()?.User != this;
SelectedItem?.GetComponent<ConnectionPanel>()?.User != this;
}
@@ -900,7 +903,14 @@ namespace Barotrauma
if (info != null)
{
LocalizedString name = Info.DisplayName;
if (controlled == null && name != Info.Name) { name += " " + TextManager.Get("Disguised"); }
if (controlled == null && name != Info.Name)
{
name += " " + TextManager.Get("Disguised");
}
else if (Info.Title != null)
{
name += '\n' + Info.Title;
}
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;

View File

@@ -1,5 +1,6 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Tutorials;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -108,7 +109,7 @@ namespace Barotrauma
private static bool ShouldDrawInventory(Character character)
{
var controller = character.SelectedConstruction?.GetComponent<Controller>();
var controller = character.SelectedItem?.GetComponent<Controller>();
return
character?.Inventory != null &&
@@ -173,8 +174,8 @@ namespace Barotrauma
{
if (character.Info != null && !character.ShouldLockHud() && character.SelectedCharacter == null && Screen.Selected != GameMain.SubEditorScreen)
{
bool mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && GUI.MouseOn == null;
if (mouseOnPortrait && PlayerInput.PrimaryMouseButtonClicked())
bool mouseOnPortrait = MouseOnCharacterPortrait() && GUI.MouseOn == null;
if (mouseOnPortrait && PlayerInput.PrimaryMouseButtonClicked() && Inventory.DraggingItems.None())
{
CharacterHealth.OpenHealthWindow = character.CharacterHealth;
}
@@ -398,32 +399,63 @@ namespace Barotrauma
progressBar.Draw(spriteBatch, cam);
}
foreach (Character npc in Character.CharacterList)
void DrawInteractionIcon(Entity entity, Identifier iconStyle)
{
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None || npc.Submarine != character.Submarine || npc.IsDead || npc.IsIncapacitated) { continue; }
if (entity == null || entity.Removed) { return; }
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)
Hull currentHull = entity switch
{
//TODO: we could probably do better than just hardcoding
//a check for InteractionType.Examine here.
Character character => character.CurrentHull,
Item item => item.CurrentHull,
_ => null
};
Range<float> visibleRange = new Range<float>(currentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, float.PositiveInfinity);
LocalizedString label = null;
if (entity is Character characterEntity)
{
if (characterEntity.IsDead || characterEntity.IsIncapacitated) { return; }
if (characterEntity?.CampaignInteractionType == CampaignMode.InteractionType.Examine)
{
//TODO: we could probably do better than just hardcoding
//a check for InteractionType.Examine here.
if (Vector2.DistanceSquared(character.Position, npc.Position) > 500f * 500f) { continue; }
if (Vector2.DistanceSquared(character.Position, entity.Position) > 500f * 500f) { return; }
var body = Submarine.CheckVisibility(character.SimPosition, npc.SimPosition, ignoreLevel: true);
if (body != null && body.UserData as Character != npc) { continue; }
var body = Submarine.CheckVisibility(character.SimPosition, entity.SimPosition, ignoreLevel: true);
if (body != null && body.UserData != entity) { return; }
visibleRange = new Range<float>(-100f, 500f);
visibleRange = new Range<float>(-100f, 500f);
}
label = characterEntity?.Info?.Title;
}
if (GUIStyle.GetComponentStyle(iconStyle) is not GUIComponentStyle style) { return; }
float dist = Vector2.Distance(character.WorldPosition, entity.WorldPosition);
float distFactor = 1.0f - MathUtils.InverseLerp(1000.0f, 3000.0f, dist);
float alpha = MathHelper.Lerp(0.3f, 1.0f, distFactor);
GUI.DrawIndicator(
spriteBatch,
npc.WorldPosition,
entity.WorldPosition,
cam,
visibleRange,
iconStyle.GetDefaultSprite(),
iconStyle.Color);
style.GetDefaultSprite(),
style.Color * alpha,
label: label);
}
foreach (Character npc in Character.CharacterList)
{
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None) { continue; }
DrawInteractionIcon(npc, ("CampaignInteractionIcon." + npc.CampaignInteractionType).ToIdentifier());
}
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial is not null)
{
foreach (var (entity, iconStyle) in tutorialMode.Tutorial.Icons)
{
DrawInteractionIcon(entity, iconStyle);
}
}
foreach (Item item in Item.ItemList)
@@ -436,10 +468,10 @@ namespace Barotrauma
}
}
if (character.SelectedConstruction != null &&
(character.CanInteractWith(character.SelectedConstruction) || Screen.Selected == GameMain.SubEditorScreen))
if (character.SelectedItem != null &&
(character.CanInteractWith(character.SelectedItem) || Screen.Selected == GameMain.SubEditorScreen))
{
character.SelectedConstruction.DrawHUD(spriteBatch, cam, character);
character.SelectedItem.DrawHUD(spriteBatch, cam, character);
}
if (character.Inventory != null)
{
@@ -487,7 +519,7 @@ namespace Barotrauma
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();
mouseOnPortrait = MouseOnCharacterPortrait() && !character.ShouldLockHud();
if (mouseOnPortrait)
{
GUIStyle.UIGlow.Draw(spriteBatch, HUDLayoutSettings.BottomRightInfoArea, GUIStyle.Green * 0.5f);
@@ -534,6 +566,13 @@ namespace Barotrauma
}
}
public static bool MouseOnCharacterPortrait()
{
if (Character.Controlled == null) { return false; }
if (CharacterHealth.OpenHealthWindow != null || Character.Controlled.SelectedCharacter != null) { return false; }
return HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition);
}
private static void DrawCharacterHoverTexts(SpriteBatch spriteBatch, Camera cam, Character character)
{
var allItems = character.Inventory?.AllItems;
@@ -561,9 +600,15 @@ namespace Barotrauma
Color nameColor = character.FocusedCharacter.GetNameColor();
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
textPos.X += 10.0f * GUI.Scale;
textPos.Y += GUIStyle.SubHeadingFont.MeasureString(focusName).Y;
if (character.FocusedCharacter.Info?.Title != null && !character.FocusedCharacter.Info.Title.IsNullOrEmpty())
{
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.Info.Title, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
textPos.Y += GUIStyle.SubHeadingFont.MeasureString(character.FocusedCharacter.Info.Title.Value).Y;
}
textPos.X += 10.0f * GUI.Scale;
if (!character.FocusedCharacter.IsIncapacitated && character.FocusedCharacter.IsPet)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("PlayHint", InputType.Use),

View File

@@ -102,7 +102,7 @@ namespace Barotrauma
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(" ".ToIdentifier(), "".ToIdentifier()))),
TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), PersonalityTrait.DisplayName),
font: font)
{
Padding = Vector4.Zero
@@ -521,29 +521,28 @@ namespace Barotrauma
Color skinColor = inc.ReadColorR8G8B8();
Color hairColor = inc.ReadColorR8G8B8();
Color facialHairColor = inc.ReadColorR8G8B8();
string ragdollFile = inc.ReadString();
string jobIdentifier = inc.ReadString();
string ragdollFile = inc.ReadString();
Identifier npcId = inc.ReadIdentifier();
uint jobIdentifier = inc.ReadUInt32();
int variant = inc.ReadByte();
JobPrefab jobPrefab = null;
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++)
if (jobIdentifier > 0)
{
jobPrefab = JobPrefab.Prefabs.Find(jp => jp.UintIdentifier == jobIdentifier);
foreach (SkillPrefab skillPrefab in jobPrefab.Skills.OrderBy(s => s.Identifier))
{
Identifier skillIdentifier = inc.ReadIdentifier();
float skillLevel = inc.ReadSingle();
skillLevels.Add(skillIdentifier, skillLevel);
}
}
skillLevels.Add(skillPrefab.Identifier, skillLevel);
}
}
// TODO: animations
CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant)
CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant, npcIdentifier: npcId)
{
ID = infoID,
ID = infoID
};
ch.RecreateHead(tagSet.ToImmutableHashSet(), hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
ch.Head.SkinColor = skinColor;
@@ -777,7 +776,21 @@ namespace Barotrauma
createColorSelector($"Customization.{nameof(info.Head.SkinColor)}".ToIdentifier(), info.SkinColors, () => info.Head.SkinColor,
(color) => info.Head.SkinColor = color);
#if DEBUG
new GUIButton(new RectTransform(Vector2.One * 0.12f,
parentComponent.RectTransform,
anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
{ RelativeOffset = new Vector2(0.01f, 0.005f) }, style: "SaveButton", color: Color.Magenta)
{
ToolTip = "DEBUG ONLY: copy the character info XML to clipboard",
OnClicked = (button, o) =>
{
XElement element = info.Save(null);
Clipboard.SetText(element.ToString());
return false;
}
};
#endif
RandomizeButton = new GUIButton(new RectTransform(Vector2.One * 0.12f,
parentComponent.RectTransform,
anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)

View File

@@ -1,8 +1,8 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
@@ -44,7 +44,8 @@ namespace Barotrauma
LastNetworkUpdateID,
AnimController.TargetDir,
SelectedCharacter,
SelectedConstruction,
SelectedItem,
SelectedSecondaryItem,
AnimController.Anim);
memLocalState.Add(posInfo);
@@ -114,34 +115,34 @@ namespace Barotrauma
public void ClientWriteInput(IWriteMessage msg)
{
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
msg.WriteByte((byte)ClientNetObject.CHARACTER_INPUT);
if (memInput.Count > 60)
{
memInput.RemoveRange(60, memInput.Count - 60);
}
msg.Write(LastNetworkUpdateID);
msg.WriteUInt16(LastNetworkUpdateID);
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
msg.Write(inputCount);
msg.WriteByte(inputCount);
for (int i = 0; i < inputCount; i++)
{
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
msg.Write(memInput[i].intAim);
msg.WriteUInt16(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);
msg.WriteUInt16(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)}"); }
if (extraData is not 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)
@@ -150,16 +151,16 @@ namespace Barotrauma
Inventory.ClientEventWrite(msg, inventoryStateEventData);
break;
case TreatmentEventData _:
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
msg.WriteBoolean(AnimController.Anim == AnimController.Animation.CPR);
break;
case CharacterStatusEventData _:
//do nothing
break;
case UpdateTalentsEventData _:
msg.Write((ushort)characterTalents.Count);
msg.WriteUInt16((ushort)characterTalents.Count);
foreach (var unlockedTalent in characterTalents)
{
msg.Write(unlockedTalent.Prefab.UintIdentifier);
msg.WriteUInt32(unlockedTalent.Prefab.UintIdentifier);
}
break;
default:
@@ -175,6 +176,11 @@ namespace Barotrauma
AnimController.Frozen = false;
Enabled = true;
//if we start receiving position updates, it means the character's no longer disabled
if (DisabledByEvent && !Removed)
{
DisabledByEvent = false;
}
UInt16 networkUpdateID = 0;
if (msg.ReadBoolean())
@@ -219,15 +225,17 @@ namespace Barotrauma
bool entitySelected = msg.ReadBoolean();
Character selectedCharacter = null;
Item selectedItem = null;
Item selectedItem = null, selectedSecondaryItem = null;
AnimController.Animation animation = AnimController.Animation.None;
if (entitySelected)
{
ushort characterID = msg.ReadUInt16();
ushort itemID = msg.ReadUInt16();
ushort secondaryItemID = msg.ReadUInt16();
selectedCharacter = FindEntityByID(characterID) as Character;
selectedItem = FindEntityByID(itemID) as Item;
selectedSecondaryItem = FindEntityByID(secondaryItemID) as Item;
if (characterID != NullEntityID)
{
bool doingCpr = msg.ReadBoolean();
@@ -274,7 +282,7 @@ namespace Barotrauma
pos, rotation,
networkUpdateID,
facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation);
selectedCharacter, selectedItem, selectedSecondaryItem, animation);
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
index++;
@@ -286,7 +294,7 @@ namespace Barotrauma
pos, rotation,
linearVelocity, angularVelocity,
sendingTime, facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation);
selectedCharacter, selectedItem, selectedSecondaryItem, animation);
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
index++;
@@ -375,9 +383,15 @@ namespace Barotrauma
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);
//it's possible to get these errors when mid-round syncing, as the client may not
//yet know about afflictions that have given the character extra limbs (e.g. spineling genes)
//ignoring the error should be safe though, not executing the attack should not cause any further issues
if (!GameMain.Client.MidRoundSyncing)
{
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];
@@ -458,25 +472,11 @@ namespace Barotrauma
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;
}
}
ReadItemTeamChange(msg, true);
break;
case EventType.RemoveFromCrew:
GameMain.GameSession.CrewManager.RemoveCharacter(this, removeInfo: true);
ReadItemTeamChange(msg, false);
break;
case EventType.UpdateExperience:
int experienceAmount = msg.ReadInt32();
@@ -507,9 +507,27 @@ namespace Barotrauma
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
}
break;
}
msg.ReadPadBits();
static void ReadItemTeamChange(IReadMessage msg, bool allowStealing)
{
var itemTeamChange = INetSerializableStruct.Read<ItemTeamChange>(msg);
foreach (var itemID in itemTeamChange.ItemIds)
{
if (FindEntityByID(itemID) is not Item item) { continue; }
item.AllowStealing = allowStealing;
if (item.GetComponent<WifiComponent>() is { } wifiComponent)
{
wifiComponent.TeamID = itemTeamChange.TeamId;
}
if (item.GetComponent<IdCard>() is { } idCard)
{
idCard.TeamID = itemTeamChange.TeamId;
idCard.SubmarineSpecificID = 0;
}
}
}
}
public static Character ReadSpawnData(IReadMessage inc)
@@ -526,6 +544,7 @@ namespace Barotrauma
Vector2 position = new Vector2(inc.ReadSingle(), inc.ReadSingle());
bool enabled = inc.ReadBoolean();
bool disabledByEvent = inc.ReadBoolean();
DebugConsole.Log("Received spawn data for " + speciesName);
@@ -561,7 +580,7 @@ namespace Barotrauma
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
try
{
character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi);
character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.SessionId != ownerId, hasAi: hasAi);
}
catch (Exception e)
{
@@ -642,7 +661,7 @@ namespace Barotrauma
GameMain.GameSession.CrewManager.AddCharacter(character);
}
if (GameMain.Client.ID == ownerId)
if (GameMain.Client.SessionId == ownerId)
{
GameMain.Client.HasSpawned = true;
GameMain.Client.Character = character;
@@ -659,7 +678,14 @@ namespace Barotrauma
}
}
character.Enabled = Controlled == character || enabled;
if (disabledByEvent)
{
character.DisabledByEvent = true;
}
else
{
character.Enabled = Controlled == character || enabled;
}
return character;
}
@@ -673,16 +699,17 @@ namespace Barotrauma
AfflictionPrefab causeOfDeathAffliction = null;
if (causeOfDeathType == CauseOfDeathType.Affliction)
{
string afflictionName = msg.ReadString();
if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionName))
uint afflictionId = msg.ReadUInt32();
AfflictionPrefab afflictionPrefab = AfflictionPrefab.Prefabs.Find(p => p.UintIdentifier == afflictionId);
if (afflictionPrefab == null)
{
string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction not found ({afflictionName})";
string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction not found (id {afflictionId})";
causeOfDeathType = CauseOfDeathType.Unknown;
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionNotFound", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
}
else
{
causeOfDeathAffliction = AfflictionPrefab.Prefabs[afflictionName];
causeOfDeathAffliction = afflictionPrefab;
}
}
bool containsAfflictionData = msg.ReadBoolean();

View File

@@ -162,11 +162,7 @@ namespace Barotrauma
openHealthWindow.characterName.Text = value.Character.Info.DisplayName;
value.Character.Info.CheckDisguiseStatus(false);
}
if (Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedConstruction.GetComponent<Ladder>() == null)
{
Character.Controlled.SelectedConstruction = null;
}
Character.Controlled.SelectedItem = null;
}
HintManager.OnShowHealthInterface();
@@ -284,6 +280,7 @@ namespace Barotrauma
cprButton = new GUIButton(new RectTransform(new Vector2(0.17f, 0.17f), characterIndicatorArea.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.Smallest), text: "", style: "CPRButton")
{
UserData = UIHighlightAction.ElementId.CPRButton,
OnClicked = (button, userData) =>
{
Character selectedCharacter = Character.Controlled?.SelectedCharacter;
@@ -694,7 +691,7 @@ namespace Barotrauma
{
distortTimer = (distortTimer + deltaTime * distortSpeed) % MathHelper.TwoPi;
Character.BlurStrength = (float)(Math.Sin(distortTimer) + 1.5f) * 0.25f * blurStrength;
Character.DistortStrength = (float)(Math.Sin(distortTimer) + 1.0f) * 0.1f * distortStrength;
Character.DistortStrength = (float)(Math.Sin(distortTimer) + 1.0f) * 0.05f * distortStrength;
}
else
{
@@ -724,7 +721,7 @@ namespace Barotrauma
//emulate a Health input to get the character to deselect the item server-side
if (GameMain.Client != null)
{
Character.Controlled.Keys[(int)InputType.Health].Hit = true;
Character.Controlled.EmulateInput(InputType.Health);
}
OpenHealthWindow = null;
}
@@ -1298,6 +1295,7 @@ namespace Barotrauma
Dictionary<Identifier, float> treatmentSuitability = new Dictionary<Identifier, float>();
GetSuitableTreatments(treatmentSuitability,
normalize: true,
user: Character.Controlled,
ignoreHiddenAfflictions: true,
limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex));
@@ -1341,13 +1339,13 @@ namespace Barotrauma
};
var innerFrame = new GUIButton(new RectTransform(Vector2.One, itemSlot.RectTransform, Anchor.Center, Pivot.Center, scaleBasis: ScaleBasis.Smallest), style: "SubtreeHeader")
{
{
UserData = item,
DisabledColor = Color.White * 0.1f,
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (!(userdata is ItemPrefab itemPrefab)) { return false; }
if (userdata is not ItemPrefab itemPrefab) { return false; }
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);
@@ -1465,7 +1463,7 @@ namespace Barotrauma
description.RectTransform.Resize(new Point(description.Rect.Width, (int)(description.TextSize.Y + 10)));
int vitalityDecrease = (int)affliction.GetVitalityDecrease(this);
int vitalityDecrease = (int)GetVitalityDecreaseWithVitalityMultipliers(affliction);
if (vitalityDecrease == 0)
{
vitality.Visible = false;
@@ -1507,7 +1505,7 @@ namespace Barotrauma
foreach (Affliction affliction in afflictions)
{
float afflictionVitalityDecrease = affliction.GetVitalityDecrease(this);
float afflictionVitalityDecrease = GetVitalityDecreaseWithVitalityMultipliers(affliction);
Color afflictionEffectColor = Color.White;
if (afflictionVitalityDecrease > 0.0f)
{
@@ -1590,7 +1588,7 @@ namespace Barotrauma
affliction.Strength / affliction.Prefab.MaxStrength);
var vitalityText = labelContainer.GetChildByUserData("vitality") as GUITextBlock;
int vitalityDecrease = (int)affliction.GetVitalityDecrease(this);
int vitalityDecrease = (int)GetVitalityDecreaseWithVitalityMultipliers(affliction);
if (vitalityDecrease == 0)
{
vitalityText.Visible = false;
@@ -1608,7 +1606,7 @@ namespace Barotrauma
{
//items can be dropped outside the health window
if (!ignoreMousePos &&
!healthWindow.Rect.Contains(PlayerInput.MousePosition) )
!healthWindow.Rect.Contains(PlayerInput.MousePosition))
{
return false;
}
@@ -1624,10 +1622,10 @@ namespace Barotrauma
}
}
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex);
Limb targetLimb =
Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex) ??
Character.AnimController.MainLimb;
item.ApplyTreatment(Character.Controlled, Character, targetLimb);
return true;
}
private void UpdateLimbIndicators(float deltaTime, Rectangle drawArea)
@@ -1690,7 +1688,7 @@ namespace Barotrauma
if (!affliction.ShouldShowIcon(Character)) { continue; }
if (!affliction.Prefab.IsBuff)
{
negativeEffect += affliction.Strength;
negativeEffect += affliction.Strength * GetVitalityMultiplier(affliction, limbHealth);
}
else
{
@@ -1904,6 +1902,7 @@ namespace Barotrauma
public void ClientRead(IReadMessage inc)
{
newAfflictions.Clear();
newPeriodicEffects.Clear();
byte afflictionCount = inc.ReadByte();
for (int i = 0; i < afflictionCount; i++)
{
@@ -1925,7 +1924,7 @@ namespace Barotrauma
int periodicAfflictionCount = inc.ReadByte();
for (int j = 0; j < periodicAfflictionCount; j++)
{
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
float periodicAfflictionTimer = inc.ReadRangedSingle(0, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
}
newAfflictions.Add((null, afflictionPrefab, afflictionStrength));
@@ -1995,13 +1994,13 @@ namespace Barotrauma
//timer has wrapped around, apply the effect
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
foreach (StatusEffect effect in periodicEffect.effect.StatusEffects)
{
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(limb));
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
}
}
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
}
}
@@ -2014,7 +2013,7 @@ namespace Barotrauma
FaceTint = DefaultFaceTint;
BodyTint = Color.TransparentBlack;
if (!(Character?.Params?.Health.ApplyAfflictionColors ?? false)) { return; }
if (!Character.Params.Health.ApplyAfflictionColors) { return; }
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{
@@ -2031,15 +2030,21 @@ namespace Barotrauma
foreach (Limb limb in Character.AnimController.Limbs)
{
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count) { continue; }
limb.BurnOverlayStrength = 0.0f;
limb.DamageOverlayStrength = 0.0f;
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{
if (kvp.Value != limbHealths[limb.HealthIndex]) { continue; }
var affliction = kvp.Key;
limb.BurnOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.BurnOverlayAlpha;
limb.DamageOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.DamageOverlayAlpha;
float burnStrength = affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.BurnOverlayAlpha;
if (kvp.Value == limbHealths[limb.HealthIndex])
{
limb.BurnOverlayStrength += burnStrength;
limb.DamageOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.DamageOverlayAlpha;
}
else
{
limb.BurnOverlayStrength += burnStrength / 2;
}
}
}
}

View File

@@ -481,7 +481,7 @@ namespace Barotrauma
{
ContentPath texturePath =
character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage)
?? ContentPath.FromRaw(character.Prefab.ContentPackage, spriteParams.GetTexturePath());
?? ContentPath.FromRaw(spriteParams.Element.ContentPackage ?? character.Prefab.ContentPackage, spriteParams.GetTexturePath());
path = GetSpritePath(texturePath);
}
else

View File

@@ -63,7 +63,7 @@ namespace Barotrauma
files = contentPackage.Files.Select(File.FromContentFile).ToList();
ModVersion = IncrementModVersion(contentPackage.ModVersion);
IsCore = contentPackage is CorePackage;
SteamWorkshopId = contentPackage.SteamWorkshopId;
UgcId = contentPackage.UgcId;
ExpectedHash = contentPackage.Hash;
InstallTime = contentPackage.InstallTime;
}
@@ -74,7 +74,7 @@ namespace Barotrauma
get => name;
set
{
var charsToRemove = Path.GetInvalidFileNameChars();
var charsToRemove = Path.GetInvalidFileNameCharsCrossPlatform();
name = string.Concat(value.Where(c => !charsToRemove.Contains(c)));
}
}
@@ -90,9 +90,9 @@ namespace Barotrauma
public bool IsCore = false;
public UInt64 SteamWorkshopId = 0;
public Option<ContentPackageId> UgcId = Option<ContentPackageId>.None();
public DateTime? InstallTime = null;
public Option<DateTime> InstallTime = Option<DateTime>.None();
public bool HasFile(File file)
=> Files.Any(f =>
@@ -120,7 +120,7 @@ namespace Barotrauma
public void DiscardHashAndInstallTime()
{
ExpectedHash = null;
InstallTime = null;
InstallTime = Option<DateTime>.None();
}
public static string IncrementModVersion(string modVersion)
@@ -155,11 +155,11 @@ namespace Barotrauma
addRootAttribute("name", Name);
if (!ModVersion.IsNullOrEmpty()) { addRootAttribute("modversion", ModVersion); }
addRootAttribute("corepackage", IsCore);
if (SteamWorkshopId != 0) { addRootAttribute("steamworkshopid", SteamWorkshopId); }
if (UgcId.TryUnwrap(out var ugcId) && ugcId is SteamWorkshopId steamWorkshopId) { addRootAttribute("steamworkshopid", steamWorkshopId.Value); }
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)); }
if (InstallTime.TryUnwrap(out var installTime)) { addRootAttribute("installtime", ToolBox.Epoch.FromDateTime(installTime)); }
files.ForEach(f => rootElement.Add(f.ToXElement()));

View File

@@ -45,9 +45,11 @@ namespace Barotrauma
var needInstalling = subscribedItems.Where(item
=> !WorkshopPackages.Any(p
=> item.Id == p.SteamWorkshopId
&& p.InstallTime.HasValue
&& item.LatestUpdateTime <= p.InstallTime))
=> p.UgcId.TryUnwrap(out var ugcId)
&& ugcId is SteamWorkshopId workshopId
&& item.Id == workshopId.Value
&& p.InstallTime.TryUnwrap(out var installTime)
&& item.LatestUpdateTime <= installTime))
.ToArray();
if (needInstalling.Any())
{

View File

@@ -16,7 +16,7 @@ namespace Barotrauma.Transition
/// Class dedicated to transitioning away from the old, shitty
/// Mods + Submarines folders to the new LocalMods folder
/// </summary>
public static class UgcTransition
public static class LegacySteamUgcTransition
{
private const string readmeName = "LOCALMODS_README.txt";
@@ -168,7 +168,11 @@ namespace Barotrauma.Transition
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));
addTickbox(mod.Dir, mod.Name,
ticked: !(mod.Item is { } item && ContentPackageManager.LocalPackages.Any(p =>
p.UgcId.TryUnwrap(out var ugcId)
&& ugcId is SteamWorkshopId workshopId
&& workshopId.Value == item.Id)));
}
}

View File

@@ -1,21 +1,18 @@
using Barotrauma.Items.Components;
using Barotrauma.ClientSource.Settings;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Barotrauma.Items.Components;
using Barotrauma.MapCreatures.Behavior;
using Barotrauma.Networking;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Globalization;
using FarseerPhysics;
using Barotrauma.Extensions;
using Barotrauma.Steam;
using System.Threading.Tasks;
using Barotrauma.ClientSource.Settings;
using Barotrauma.MapCreatures.Behavior;
using static Barotrauma.FabricationRecipe;
namespace Barotrauma
@@ -229,7 +226,7 @@ namespace Barotrauma
return client.HasPermission(ClientPermissions.Kick);
case "ban":
case "banip":
case "banendpoint":
case "banaddress":
return client.HasPermission(ClientPermissions.Ban);
case "unban":
case "unbanip":
@@ -432,24 +429,6 @@ namespace Barotrauma
}
}));
commands.Add(new Command("startlidgrenclient", "", (string[] args) =>
{
if (args.Length == 0) return;
if (GameMain.Client == null)
{
GameMain.Client = new GameClient("Name", args[0], 0);
}
}));
commands.Add(new Command("startsteamp2pclient", "", (string[] args) =>
{
if (GameMain.Client == null)
{
GameMain.Client = new GameClient("Name", null, 76561198977850505); //this is juan's alt account, feel free to abuse this one
}
}));
commands.Add(new Command("enablecheats", "enablecheats: Enables cheat commands and disables Steam achievements during this play session.", (string[] args) =>
{
CheatsEnabled = true;
@@ -744,7 +723,7 @@ namespace Barotrauma
AssignOnExecute("explosion", (string[] args) =>
{
Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
Vector2 explosionPos = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
float range = 500, force = 10, damage = 50, structureDamage = 20, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
if (args.Length > 0) float.TryParse(args[0], out range);
if (args.Length > 1) float.TryParse(args[1], out force);
@@ -1299,7 +1278,7 @@ namespace Barotrauma
int? fabricationCost = null;
int? deconstructProductCost = null;
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab);
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab && f.RequiredItems.Any());
if (fabricationRecipe != null)
{
foreach (var ingredient in fabricationRecipe.RequiredItems)
@@ -1334,6 +1313,21 @@ namespace Barotrauma
if (fabricationRecipe != null)
{
var ingredient = fabricationRecipe.RequiredItems.Find(r => r.ItemPrefabs.Contains(targetItem));
if (ingredient == null)
{
foreach (var requiredItem in fabricationRecipe.RequiredItems)
{
foreach (var itemPrefab2 in requiredItem.ItemPrefabs)
{
foreach (var recipe in itemPrefab2.FabricationRecipes.Values)
{
ingredient ??= recipe.RequiredItems.Find(r => r.ItemPrefabs.Contains(targetItem));
}
}
}
}
if (ingredient == null)
{
NewMessage("Deconstructing \"" + itemPrefab.Name + "\" produces \"" + deconstructItem.ItemIdentifier + "\", which isn't required in the fabrication recipe of the item.", Color.Red);
@@ -2077,7 +2071,17 @@ namespace Barotrauma
var prefab = MapEntityPrefab.Find(null, args[0]);
if (prefab != null)
{
DebugConsole.NewMessage(prefab.Name + " " + prefab.Identifier + " " + prefab.GetType().ToString());
NewMessage(prefab.Name + " " + prefab.Identifier + " " + prefab.GetType().ToString());
}
}));
commands.Add(new Command("copycharacterinfotoclipboard", "", (string[] args) =>
{
if (Character.Controlled?.Info != null)
{
XElement element = Character.Controlled?.Info.Save(null);
Clipboard.SetText(element.ToString());
DebugConsole.NewMessage($"Copied the characterinfo of {Character.Controlled.Name} to clipboard.");
}
}));
@@ -2485,29 +2489,11 @@ namespace Barotrauma
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
}));
#if DEBUG
commands.Add(new Command("playovervc", "Plays a sound over voice chat.", (args) =>
{
VoipCapture.Instance?.SetOverrideSound(args.Length > 0 ? args[0] : null);
}));
commands.Add(new Command("querylobbies", "Queries all SteamP2P lobbies", (args) =>
{
TaskPool.Add("DebugQueryLobbies",
SteamManager.LobbyQueryRequest(), (t) =>
{
t.TryGetResult(out List<Steamworks.Data.Lobby> lobbies);
foreach (var lobby in lobbies)
{
NewMessage(lobby.GetData("name") + ", " + lobby.GetData("lobbyowner"), Color.Yellow);
}
NewMessage($"Retrieved a total of {lobbies.Count} lobbies", Color.Lime);
});
}));
commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) =>
{
if (args.Length != 1) return;
if (args.Length != 1) { return; }
TextManager.CheckForDuplicates(args[0].ToIdentifier().ToLanguageIdentifier());
}));
@@ -2517,9 +2503,20 @@ namespace Barotrauma
NPCConversation.WriteToCSV();
}));
commands.Add(new Command("csvtoxml", "csvtoxml [language] -> Converts .csv localization files in Content/NPCConversations & Content/Texts to .xml for use in-game.", (string[] args) =>
commands.Add(new Command("csvtoxml", "csvtoxml -> Converts .csv localization files Content/Texts/Texts.csv and Content/Texts/NPCConversations.csv to .xml for use in-game.", (string[] args) =>
{
LocalizationCSVtoXML.Convert();
ShowQuestionPrompt("Do you want to save the text files to the project folder (../../../BarotraumaShared/Content/Texts/)? If not, they are saved in the current working directory. Y/N",
(option1) =>
{
ShowQuestionPrompt("Do you want to convert the NPC conversations as well? Y/N",
(option2) =>
{
LocalizationCSVtoXML.ConvertMasterLocalizationKit(
option1.ToLowerInvariant() == "y" ? "../../../BarotraumaShared/Content/Texts/" : "Content/Texts",
option1.ToLowerInvariant() == "y" ? "../../../BarotraumaShared/Content/NPCConversations/" : "Content/NPCConversations",
convertConversations: option2.ToLowerInvariant() == "y");
});
});
}));
commands.Add(new Command("printproperties", "Goes through the currently collected property list for missing localizations and writes them to a file.", (string[] args) =>
@@ -2816,7 +2813,7 @@ namespace Barotrauma
);
AssignOnClientExecute(
"banendpoint|banip",
"banaddress|banip",
(string[] args) =>
{
if (GameMain.Client == null || args.Length == 0) return;
@@ -2838,7 +2835,7 @@ namespace Barotrauma
}
GameMain.Client?.SendConsoleCommand(
"banendpoint " +
"banaddress " +
args[0] + " " +
(banDuration.HasValue ? banDuration.Value.TotalSeconds.ToString() : "0") + " " +
reason);
@@ -2851,13 +2848,16 @@ namespace Barotrauma
{
if (GameMain.Client == null || args.Length == 0) return;
string clientName = string.Join(" ", args);
GameMain.Client.UnbanPlayer(clientName, "");
GameMain.Client.UnbanPlayer(clientName);
}));
commands.Add(new Command("unbanip", "unbanip [ip]: Unban a specific IP.", (string[] args) =>
commands.Add(new Command("unbanaddress", "unbanaddress [endpoint]: Unban a specific endpoint.", (string[] args) =>
{
if (GameMain.Client == null || args.Length == 0) return;
GameMain.Client.UnbanPlayer("", args[0]);
if (Endpoint.Parse(args[0]).TryUnwrap(out var endpoint))
{
GameMain.Client.UnbanPlayer(endpoint);
}
}));
AssignOnClientExecute(

View File

@@ -32,12 +32,12 @@ namespace Barotrauma
private static bool shouldFadeToBlack;
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> _)
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> _, float duration)
{
return
lastActiveAction != null &&
lastActiveAction.ParentEvent != ParentEvent &&
Timing.TotalTime < lastActiveAction.lastActiveTime + BlockOtherConversationsDuration;
Timing.TotalTime < lastActiveAction.lastActiveTime + duration;
}
partial void ShowDialog(Character speaker, Character targetCharacter)
@@ -63,33 +63,45 @@ namespace Barotrauma
shouldFadeToBlack = fadeToBlack;
Sprite eventSprite = EventSet.GetEventSprite(spriteIdentifier);
if (lastMessageBox != null && !lastMessageBox.Closed && GUIMessageBox.MessageBoxes.Contains(lastMessageBox))
{
if (actionId != null && lastMessageBox.UserData is Pair<string, ushort> userData)
if (eventSprite != null && lastMessageBox.BackgroundIcon == null)
{
if (userData.Second == actionId) { return; }
lastMessageBox.UserData = new Pair<string, ushort>("ConversationAction", actionId.Value);
//no background icon in the last message box: we need to create a new one
lastMessageBox.Close();
}
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
Debug.Assert(conversationList != null);
// gray out the last text block
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
else
{
if (lastElement.FindChild("text", true) is GUITextBlock textLayout)
if (actionId != null && lastMessageBox.UserData is Pair<string, ushort> userData)
{
textLayout.OverrideTextColor(Color.DarkGray * 0.8f);
if (userData.Second == actionId) { return; }
lastMessageBox.UserData = new Pair<string, ushort>("ConversationAction", actionId.Value);
}
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
Debug.Assert(conversationList != null);
// gray out the last text block
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
{
if (lastElement.FindChild("text", true) is GUITextBlock textLayout)
{
textLayout.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
float prevSize = conversationList.TotalSize;
List<GUIButton> extraButtons = CreateConversation(conversationList, text, speaker, options, string.IsNullOrWhiteSpace(spriteIdentifier));
AssignActionsToButtons(extraButtons, lastMessageBox);
RecalculateLastMessage(conversationList, true);
conversationList.BarScroll = (prevSize - conversationList.Content.Rect.Height) / (conversationList.TotalSize - conversationList.Content.Rect.Height);
conversationList.ScrollToEnd(duration: 0.5f);
lastMessageBox.SetBackgroundIcon(eventSprite);
return;
}
List<GUIButton> extraButtons = CreateConversation(conversationList, text, speaker, options, string.IsNullOrWhiteSpace(spriteIdentifier));
AssignActionsToButtons(extraButtons, lastMessageBox);
RecalculateLastMessage(conversationList, true);
conversationList.ScrollToEnd(0.5f);
lastMessageBox.SetBackgroundIcon(EventSet.GetEventSprite(spriteIdentifier));
return;
}
var (relative, min) = GetSizes(dialogType);
@@ -100,7 +112,10 @@ namespace Barotrauma
{
UserData = "ConversationAction"
};
messageBox.OnAddedToGUIUpdateList += (GUIComponent component) =>
{
if (Screen.Selected is not GameScreen) { messageBox.Close(); }
};
lastMessageBox = messageBox;
messageBox.InnerFrame.ClearChildren();
@@ -308,7 +323,7 @@ namespace Barotrauma
AlwaysOverrideCursor = true
};
LocalizedString translatedText = TextManager.Get(text).Fallback(text);
LocalizedString translatedText = TextManager.ParseInputTypes(TextManager.Get(text)).Fallback(text);
if (speaker?.Info != null && drawChathead)
{
@@ -368,18 +383,18 @@ namespace Barotrauma
private static void SendResponse(UInt16 actionId, int selectedOption)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ClientPacketHeader.EVENTMANAGER_RESPONSE);
outmsg.Write(actionId);
outmsg.Write((byte)selectedOption);
outmsg.WriteByte((byte)ClientPacketHeader.EVENTMANAGER_RESPONSE);
outmsg.WriteUInt16(actionId);
outmsg.WriteByte((byte)selectedOption);
GameMain.Client?.ClientPeer?.Send(outmsg, DeliveryMethod.Reliable);
}
private static void SendIgnore(UInt16 actionId)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ClientPacketHeader.EVENTMANAGER_RESPONSE);
outmsg.Write(actionId);
outmsg.Write(byte.MaxValue);
outmsg.WriteByte((byte)ClientPacketHeader.EVENTMANAGER_RESPONSE);
outmsg.WriteUInt16(actionId);
outmsg.WriteByte(byte.MaxValue);
GameMain.Client?.ClientPeer?.Send(outmsg, DeliveryMethod.Reliable);
}

View File

@@ -0,0 +1,58 @@
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma;
partial class InventoryHighlightAction : EventAction
{
private static readonly Color highlightColor = Color.Orange;
partial void UpdateProjSpecific()
{
foreach (var target in ParentEvent.GetTargets(TargetTag))
{
SetHighlight(target);
}
}
private void SetHighlight(Entity entity)
{
if (entity is Item item)
{
int i = 0;
foreach (var itemContainer in item.GetComponents<Items.Components.ItemContainer>())
{
if (ItemContainerIndex == -1 || i == ItemContainerIndex)
{
SetHighlight(itemContainer.Inventory);
}
i++;
}
}
else if (entity is Character c)
{
SetHighlight(c.Inventory);
}
}
private void SetHighlight(Inventory inventory)
{
if (inventory?.visualSlots == null) { return; }
for (int i = 0; i < inventory.visualSlots.Length; i++)
{
if (inventory.visualSlots[i].HighlightTimer > 0) { continue; }
Item item = inventory.GetItemAt(i);
if (IsSuitableItem(item) ||
(Recursive && item?.OwnInventory != null && item.OwnInventory.FindAllItems(it => IsSuitableItem(it), recursive: true).Any()))
{
inventory.visualSlots[i].ShowBorderHighlight(highlightColor, 0.5f, 0.5f, 0.1f);
}
}
}
private bool IsSuitableItem(Item item)
{
return (ItemIdentifier.IsEmpty && item == null) ||
(item != null && (item.Prefab.Identifier == ItemIdentifier || item.HasTag(ItemIdentifier)));
}
}

View File

@@ -0,0 +1,77 @@
using Barotrauma.Tutorials;
using System;
using System.Linq;
namespace Barotrauma;
partial class MessageBoxAction : EventAction
{
partial void UpdateProjSpecific()
{
if (Type == ActionType.Create || Type == ActionType.ConnectObjective)
{
CreateMessageBox();
if (!ObjectiveTag.IsEmpty && GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
{
Identifier id = Identifier.IfEmpty(Text);
var segment = Tutorial.Segment.CreateMessageBoxSegment(id, ObjectiveTag, CreateMessageBox);
tutorialMode.Tutorial?.TriggerTutorialSegment(segment, connectObjective: Type == ActionType.ConnectObjective);
}
}
else if (Type == ActionType.Close)
{
GUIMessageBox.Close(Tag);
}
else if (Type == ActionType.Clear)
{
GUIMessageBox.CloseAll();
}
}
public void CreateMessageBox()
{
new GUIMessageBox(
headerText: TextManager.Get(Header),
text: RichString.Rich(TextManager.ParseInputTypes(TextManager.Get(Text).Fallback(Text.ToString()), useColorHighlight: true)),
buttons: Array.Empty<LocalizedString>(),
type: GUIMessageBox.Type.Tutorial,
tag: Tag,
iconStyle: IconStyle,
autoCloseCondition: GetAutoCloseCondition(),
hideCloseButton: HideCloseButton)
{
FlashOnAutoCloseCondition = true
};
}
private Func<bool> GetAutoCloseCondition()
{
var character = ParentEvent.GetTargets(TargetTag).FirstOrDefault() as Character;
Func<bool> autoCloseCondition = null;
if (!string.IsNullOrEmpty(CloseOnInput) && Enum.TryParse(CloseOnInput, true, out InputType closeOnInput))
{
autoCloseCondition = () => PlayerInput.KeyDown(closeOnInput);
}
else if (!CloseOnSelectTag.IsEmpty)
{
autoCloseCondition = () => character?.SelectedItem != null && character.SelectedItem.HasTag(CloseOnSelectTag);
}
else if (!CloseOnPickUpTag.IsEmpty)
{
autoCloseCondition = () => character?.Inventory != null && character.Inventory.FindItemByTag(CloseOnPickUpTag, recursive: true) != null;
}
else if (!CloseOnEquipTag.IsEmpty)
{
autoCloseCondition = () => character != null && character.HasEquippedItem(CloseOnEquipTag);
}
else if (!CloseOnExitRoomName.IsEmpty)
{
autoCloseCondition = () => character?.CurrentHull == null || character.CurrentHull.RoomName.ToIdentifier() != CloseOnExitRoomName;
}
else if (!CloseOnInRoomName.IsEmpty)
{
autoCloseCondition = () => character?.CurrentHull != null && character.CurrentHull.RoomName.ToIdentifier() == CloseOnInRoomName;
}
return autoCloseCondition;
}
}

View File

@@ -0,0 +1,51 @@
using Microsoft.Xna.Framework;
namespace Barotrauma;
partial class TutorialHighlightAction : EventAction
{
private static readonly Color highlightColor = Color.Orange;
partial void UpdateProjSpecific()
{
if (GameMain.GameSession?.GameMode is not TutorialMode) { return; }
foreach (var target in ParentEvent.GetTargets(TargetTag))
{
SetHighlight(target);
}
}
private void SetHighlight(Entity entity)
{
if (entity is Item i)
{
SetItemHighlight(i);
}
else if (entity is Structure s)
{
SetStructureHighlight(s);
}
else if (entity is Character c)
{
SetCharacterHighlight(c);
}
}
private void SetItemHighlight(Item item)
{
if (item.ExternalHighlight == State) { return; }
item.HighlightColor = State ? highlightColor : null;
item.ExternalHighlight = State;
}
private void SetStructureHighlight(Structure structure)
{
structure.SpriteColor = State ? highlightColor : Color.White;
structure.ExternalHighlight = State;
}
private void SetCharacterHighlight(Character character)
{
character.ExternalHighlight = State;
}
}

View File

@@ -0,0 +1,50 @@
using Barotrauma.Tutorials;
namespace Barotrauma;
partial class TutorialSegmentAction : EventAction
{
private Tutorial.Segment segment;
partial void UpdateProjSpecific()
{
// Only need to create the segment when it's being triggered (otherwise the tutorial already has the segment instance)
if (Type == SegmentActionType.Trigger)
{
segment = Tutorial.Segment.CreateInfoBoxSegment(Identifier, ObjectiveTag, AutoPlayVideo ? Tutorials.AutoPlayVideo.Yes : Tutorials.AutoPlayVideo.No,
new Tutorial.Segment.Text(TextTag, Width, Height, Anchor.Center),
new Tutorial.Segment.Video(VideoFile, TextTag, Width, Height));
}
else if (Type == SegmentActionType.Add)
{
segment = Tutorial.Segment.CreateObjectiveSegment(Identifier, !ObjectiveTag.IsEmpty ? ObjectiveTag : Identifier);
}
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
{
if (tutorialMode.Tutorial is Tutorial tutorial)
{
switch (Type)
{
case SegmentActionType.Trigger:
case SegmentActionType.Add:
tutorial.TriggerTutorialSegment(segment);
break;
case SegmentActionType.Complete:
tutorial.CompleteTutorialSegment(Identifier);
break;
case SegmentActionType.Remove:
tutorial.RemoveTutorialSegment(Identifier);
break;
case SegmentActionType.CompleteAndRemove:
tutorial.CompleteTutorialSegment(Identifier);
tutorial.RemoveTutorialSegment(Identifier);
break;
}
}
}
else
{
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\": attempting to use TutorialSegmentAction during a non-Tutorial game mode!");
}
}
}

View File

@@ -0,0 +1,46 @@
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma;
partial class UIHighlightAction : EventAction
{
private static readonly Color highlightColor = Color.Orange;
partial void UpdateProjSpecific()
{
bool useCircularFlash = false;
GUIComponent component = null;
if (Id != ElementId.None)
{
component = GUI.GetAdditions().FirstOrDefault(c => Equals(Id, c.UserData));
}
else if (!EntityIdentifier.IsEmpty)
{
component = GUI.GetAdditions().FirstOrDefault(c =>
c.UserData is MapEntityPrefab mep && mep.Identifier == EntityIdentifier || c.UserData is MapEntity me && me.Prefab.Identifier == EntityIdentifier);
}
else if (!OrderIdentifier.IsEmpty)
{
useCircularFlash = true;
if (!OrderTargetTag.IsEmpty)
{
component =
GUI.GetAdditions().FirstOrDefault(c =>
c.UserData is CrewManager.MinimapNodeData nodeData && nodeData.Order is Order order &&
order.Identifier == OrderIdentifier && order.Option == OrderOption && order.TargetEntity is Item item && item.HasTag(OrderTargetTag));
}
component ??=
GUI.GetAdditions().FirstOrDefault(c => c.UserData is Order order && order.Identifier == OrderIdentifier && order.Option == OrderOption) ??
GUI.GetAdditions().FirstOrDefault(c => c.UserData is Order order && order.Identifier == OrderIdentifier) ??
GUI.GetAdditions().FirstOrDefault(c => Equals(OrderCategory, c.UserData));
}
if (component != null && component.FlashTimer <= 0.0f)
{
component.Flash(highlightColor, useCircularFlash: useCircularFlash);
component.Bounce |= Bounce;
}
}
}

View File

@@ -651,11 +651,11 @@ namespace Barotrauma
break;
case NetworkEventType.MISSION:
Identifier missionIdentifier = msg.ReadIdentifier();
string missionName = msg.ReadString();
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
if (prefab != null)
{
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", prefab.Name),
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", missionName),
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
{
IconColor = prefab.IconColor

View File

@@ -1,6 +1,4 @@
using Barotrauma.Networking;
namespace Barotrauma
namespace Barotrauma
{
partial class CombatMission : Mission
{
@@ -8,7 +6,7 @@ namespace Barotrauma
{
get
{
if (descriptions == null) return "";
if (descriptions == null) { return ""; }
if (GameMain.Client?.Character == null)
{

View File

@@ -80,6 +80,10 @@ namespace Barotrauma
LocalizedString header = messageIndex < Headers.Length ? Headers[messageIndex] : "";
LocalizedString message = messageIndex < Messages.Length ? Messages[messageIndex] : "";
if (!message.IsNullOrEmpty())
{
message = ModifyMessage(message);
}
CoroutineManager.StartCoroutine(ShowMessageBoxAfterRoundSummary(header, message));
}

View File

@@ -6,23 +6,26 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
using System.Threading;
using Barotrauma.Threading;
namespace Barotrauma
{
public class ScalableFont : IDisposable
{
private static List<ScalableFont> FontList = new List<ScalableFont>();
private static readonly List<ScalableFont> FontList = new List<ScalableFont>();
private static Library Lib = null;
private readonly object mutex = new object();
private static readonly object globalMutex = new object();
private readonly ReaderWriterLockSlim rwl = new ReaderWriterLockSlim();
private string filename;
private Face face;
private readonly string filename;
private readonly Face face;
private uint size;
private int baseHeight;
private Dictionary<uint, GlyphData> texCoords;
private List<Texture2D> textures;
private GraphicsDevice graphicsDevice;
private readonly Dictionary<uint, GlyphData> texCoords;
private readonly List<Texture2D> textures;
private readonly GraphicsDevice graphicsDevice;
private Vector2 currentDynamicAtlasCoords;
private int currentDynamicAtlasNextY;
@@ -49,7 +52,7 @@ namespace Barotrauma
set
{
size = value;
if (graphicsDevice != null) RenderAtlas(graphicsDevice, charRanges, texDims, baseChar);
if (graphicsDevice != null) { RenderAtlas(graphicsDevice, charRanges, texDims, baseChar); }
}
}
@@ -93,11 +96,15 @@ namespace Barotrauma
public ScalableFont(string filename, uint size, GraphicsDevice gd = null, bool dynamicLoading = false, bool isCJK = false)
{
lock (mutex)
lock (globalMutex)
{
Lib ??= new Library();
}
this.filename = filename;
this.face = null;
using (new ReadLock(rwl))
{
if (Lib == null) Lib = new Library();
this.filename = filename;
this.face = null;
foreach (ScalableFont font in FontList)
{
if (font.filename == filename)
@@ -106,19 +113,23 @@ namespace Barotrauma
break;
}
}
this.face ??= new Face(Lib, filename);
this.size = size;
this.textures = new List<Texture2D>();
this.texCoords = new Dictionary<uint, GlyphData>();
this.DynamicLoading = dynamicLoading;
this.IsCJK = isCJK;
this.graphicsDevice = gd;
}
if (gd != null && !dynamicLoading)
{
RenderAtlas(gd);
}
this.face ??= new Face(Lib, filename);
this.size = size;
this.textures = new List<Texture2D>();
this.texCoords = new Dictionary<uint, GlyphData>();
this.DynamicLoading = dynamicLoading;
this.IsCJK = isCJK;
this.graphicsDevice = gd;
if (gd != null && !dynamicLoading)
{
RenderAtlas(gd);
}
lock (globalMutex)
{
FontList.Add(this);
}
}
@@ -162,7 +173,7 @@ namespace Barotrauma
Vector2 currentCoords = Vector2.Zero;
int nextY = 0;
lock (mutex)
using (new WriteLock(rwl))
{
face.SetPixelSizes(0, size);
face.LoadGlyph(face.GetCharIndex(baseChar), LoadFlags.Default, LoadTarget.Normal);
@@ -175,19 +186,22 @@ namespace Barotrauma
for (uint j = start; j <= end; j++)
{
uint glyphIndex = face.GetCharIndex(j);
if (glyphIndex == 0) continue;
if (glyphIndex == 0)
{
texCoords.Add(j, new GlyphData(
advance: 0,
texIndex: -1));
continue;
}
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
{
if (face.Glyph.Metrics.HorizontalAdvance > 0)
{
//glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData(
advance: (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex: -1); //indicates no texture because the glyph is empty
//glyph is empty, but char might still apply advance
GlyphData blankData = new GlyphData(
advance: Math.Max((float)face.Glyph.Metrics.HorizontalAdvance, 0f),
texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(j, blankData);
}
texCoords.Add(j, blankData);
continue;
}
//stacktrace doesn't really work that well when RenderGlyph throws an exception
@@ -257,7 +271,7 @@ namespace Barotrauma
private void DynamicRenderAtlas(GraphicsDevice gd, uint character, int texDims = 1024, uint baseChar = 0x54)
{
bool missingCharacterFound = false;
lock (mutex)
using (new ReadLock(rwl))
{
missingCharacterFound = !texCoords.ContainsKey(character);
}
@@ -268,10 +282,9 @@ namespace Barotrauma
private void DynamicRenderAtlas(GraphicsDevice gd, string str, int texDims = 1024, uint baseChar = 0x54)
{
bool missingCharacterFound = false;
var distinctChrs = str.Distinct().Select(c => (uint)c).ToArray();
lock (mutex)
using (new ReadLock(rwl))
{
foreach (var character in distinctChrs)
foreach (var character in str)
{
if (texCoords.ContainsKey(character)) { continue; }
@@ -280,7 +293,7 @@ namespace Barotrauma
}
}
if (!missingCharacterFound) { return; }
DynamicRenderAtlas(gd, distinctChrs, texDims, baseChar);
DynamicRenderAtlas(gd, str.Select(c => (uint)c), texDims, baseChar);
}
private void DynamicRenderAtlas(GraphicsDevice gd, IEnumerable<uint> characters, int texDims = 1024, uint baseChar = 0x54)
@@ -299,7 +312,7 @@ namespace Barotrauma
Fixed26Dot6 horizontalAdvance;
Vector2 drawOffset;
lock (mutex)
using (new WriteLock(rwl))
{
if (textures.Count == 0)
{
@@ -318,20 +331,23 @@ namespace Barotrauma
if (texCoords.ContainsKey(character)) { continue; }
uint glyphIndex = face.GetCharIndex(character);
if (glyphIndex == 0) { continue; }
if (glyphIndex == 0)
{
texCoords.Add(character, new GlyphData(
advance: 0,
texIndex: -1));
continue;
}
face.SetPixelSizes(0, size);
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
{
if (face.Glyph.Metrics.HorizontalAdvance > 0)
{
//glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData(
advance: (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(character, blankData);
}
//glyph is empty, but char might still apply advance
GlyphData blankData = new GlyphData(
advance: Math.Max((float)face.Glyph.Metrics.HorizontalAdvance, 0f),
texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(character, blankData);
continue;
}

View File

@@ -325,6 +325,12 @@ namespace Barotrauma
ToggleOpen = PreferChatBoxOpen = GameSettings.CurrentConfig.ChatOpen;
}
public void Toggle()
{
ToggleOpen = !ToggleOpen;
CloseAfterMessageSent = false;
}
public bool TypingChatMessage(GUITextBox textBox, string text)
{
string command = ChatMessage.GetChatMessageCommand(text, out _);
@@ -611,22 +617,29 @@ namespace Barotrauma
showNewMessagesButton.Visible = false;
}
if (PlayerInput.KeyHit(InputType.ToggleChatMode) && GUI.KeyboardDispatcher.Subscriber == null && Screen.Selected == GameMain.GameScreen)
if (Screen.Selected == GameMain.GameScreen && GUI.KeyboardDispatcher.Subscriber == null)
{
try
if (PlayerInput.KeyHit(InputType.ToggleChatMode))
{
var mode = GameMain.ActiveChatMode switch
try
{
ChatMode.Local => ChatMode.Radio,
ChatMode.Radio => ChatMode.Local,
_ => throw new NotImplementedException()
};
ChatModeDropDown.SelectItem(mode);
// TODO: Play a sound?
var mode = GameMain.ActiveChatMode switch
{
ChatMode.Local => ChatMode.Radio,
ChatMode.Radio => ChatMode.Local,
_ => throw new NotImplementedException()
};
ChatModeDropDown.SelectItem(mode);
// TODO: Play a sound?
}
catch (NotImplementedException)
{
DebugConsole.ThrowError($"Error toggling chat mode: not implemented for current mode \"{GameMain.ActiveChatMode}\"");
}
}
catch (NotImplementedException)
else if (PlayerInput.KeyHit(InputType.ChatBox))
{
DebugConsole.ThrowError($"Error toggling chat mode: not implemented for current mode \"{GameMain.ActiveChatMode}\"");
Toggle();
}
}

View File

@@ -128,10 +128,11 @@ namespace Barotrauma
var sortGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), hireablesGroup.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.015f
RelativeSpacing = 0.015f,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), sortGroup.RectTransform), text: TextManager.Get("campaignstore.sortby"));
sortingDropDown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1.0f), sortGroup.RectTransform), elementCount: 5)
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sortGroup.RectTransform), text: TextManager.Get("campaignstore.sortby"));
sortingDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1.0f), sortGroup.RectTransform), elementCount: 5)
{
OnSelected = (child, userData) =>
{
@@ -193,19 +194,20 @@ namespace Barotrauma
{
RelativeSpacing = 0.01f
};
validateHiresButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
validateHiresButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
{
ClickSound = GUISoundType.ConfirmTransaction,
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"))
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.Cart,
ForceUpperCase = ForceUpperCase.Yes,
Enabled = HasPermission,
OnClicked = (b, o) => RemoveAllPendingHires()
};
GUITextBlock.AutoScaleAndNormalize(validateHiresButton.TextBlock, clearAllButton.TextBlock);
resolutionWhenCreated = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
@@ -528,7 +530,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(" ".ToIdentifier(), Identifier.Empty)));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), trait.DisplayName);
}
infoLabelGroup.Recalculate();
infoValueGroup.Recalculate();
@@ -887,35 +889,35 @@ namespace Barotrauma
if (campaign is MultiPlayerCampaign)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.CREW);
msg.WriteByte((byte)ClientPacketHeader.CREW);
msg.Write(updatePending);
msg.WriteBoolean(updatePending);
if (updatePending)
{
msg.Write((ushort)PendingHires.Count);
msg.WriteUInt16((ushort)PendingHires.Count);
foreach (CharacterInfo pendingHire in PendingHires)
{
msg.Write(pendingHire.GetIdentifierUsingOriginalName());
msg.WriteInt32(pendingHire.GetIdentifierUsingOriginalName());
}
}
msg.Write(validateHires);
msg.WriteBoolean(validateHires);
bool validRenaming = renameCharacter.info != null && !string.IsNullOrEmpty(renameCharacter.newName);
msg.Write(validRenaming);
msg.WriteBoolean(validRenaming);
if (validRenaming)
{
int identifier = renameCharacter.info.GetIdentifierUsingOriginalName();
msg.Write(identifier);
msg.Write(renameCharacter.newName);
msg.WriteInt32(identifier);
msg.WriteString(renameCharacter.newName);
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.GetIdentifierUsingOriginalName() == identifier) ?? false;
msg.Write(existingCrewMember);
msg.WriteBoolean(existingCrewMember);
}
msg.Write(firedCharacter != null);
msg.WriteBoolean(firedCharacter != null);
if (firedCharacter != null)
{
msg.Write(firedCharacter.GetIdentifier());
msg.WriteInt32(firedCharacter.GetIdentifier());
}
GameMain.Client.ClientPeer?.Send(msg, DeliveryMethod.Reliable);

View File

@@ -48,7 +48,7 @@ namespace Barotrauma
WaitingBackground = 6, // Cursor + Hourglass
}
public static class GUI
static class GUI
{
public static GUICanvas Canvas => GUICanvas.Instance;
public static CursorState MouseCursor = CursorState.Default;
@@ -410,7 +410,7 @@ namespace Barotrauma
{
y += yStep;
DrawString(spriteBatch, new Vector2(10, y),
"Sub pos: " + Submarine.MainSub.Position.ToPoint(),
"Sub pos: " + Submarine.MainSub.WorldPosition.ToPoint(),
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
}
@@ -879,6 +879,11 @@ namespace Barotrauma
}
}
}
public static IEnumerable<GUIComponent> GetAdditions()
{
return additions;
}
#endregion
public static GUIComponent MouseOn { get; private set; }
@@ -958,7 +963,7 @@ namespace Barotrauma
// Wire cursors
if (Character.Controlled != null)
{
if (Character.Controlled.SelectedConstruction?.GetComponent<ConnectionPanel>() != null)
if (Character.Controlled.SelectedItem?.GetComponent<ConnectionPanel>() != null)
{
if (Connection.DraggingConnected != null)
{
@@ -981,7 +986,7 @@ namespace Barotrauma
return editor.GetMouseCursorState();
// Portrait area during gameplay
case GameScreen _ when !(Character.Controlled?.ShouldLockHud() ?? true):
if (HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) || CharacterHealth.IsMouseOnHealthBar())
if (CharacterHUD.MouseOnCharacterPortrait() || CharacterHealth.IsMouseOnHealthBar())
{
return CursorState.Hand;
}
@@ -1018,6 +1023,11 @@ namespace Barotrauma
if (c.Enabled)
{
var dragHandle = c as GUIDragHandle ?? parent as GUIDragHandle;
if (dragHandle != null)
{
return dragHandle.Dragging ? CursorState.Dragging : CursorState.Hand;
}
// Some parent elements take priority
// but not when the child is a GUIButton or GUITickBox
if (!(parent is GUIButton) && !(parent is GUIListBox) ||
@@ -1027,6 +1037,7 @@ namespace Barotrauma
}
}
// Children in list boxes can be interacted with despite not having
// a GUIButton inside of them so instead of hard coding we check if
// the children can be interacted with by checking their hover state
@@ -1097,6 +1108,8 @@ namespace Barotrauma
return list;
case GUIScrollBar bar:
return bar;
case GUIDragHandle dragHandle:
return dragHandle;
}
}
component = parent;
@@ -1344,7 +1357,7 @@ namespace Barotrauma
/// <param name="createOffset">Should the indicator move based on the camera position?</param>
/// <param name="overrideAlpha">Override the distance-based alpha value with the specified alpha value</param>
public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Range<float> visibleRange, Sprite sprite, in Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null, LocalizedString label = null)
{
Vector2 diff = worldPosition - cam.WorldViewCenter;
float dist = diff.Length();
@@ -1394,10 +1407,6 @@ namespace Barotrauma
angle = MathHelper.Lerp(originalAngle, angle, MathHelper.Clamp(((screenDist + 10f) - iconDiff.Length()) / 10f, 0f, 1f));
/*Vector2 unclampedDiff = new Vector2(
(float)Math.Cos(angle) * screenDist,
(float)-Math.Sin(angle) * screenDist);*/
iconDiff = new Vector2(
(float)Math.Cos(angle) * Math.Min(GameMain.GraphicsWidth * 0.4f, screenDist),
(float)-Math.Sin(angle) * Math.Min(GameMain.GraphicsHeight * 0.4f, screenDist));
@@ -1405,7 +1414,20 @@ namespace Barotrauma
Vector2 iconPos = cam.WorldToScreen(cam.WorldViewCenter) + iconDiff;
sprite.Draw(spriteBatch, iconPos, color * alpha, rotate: 0.0f, scale: symbolScale);
if (/*unclampedDiff.Length()*/ screenDist - 10 > iconDiff.Length())
if (label != null)
{
float cursorDist = Vector2.Distance(PlayerInput.MousePosition, iconPos);
if (cursorDist < sprite.size.X * symbolScale)
{
Vector2 textSize = GUIStyle.Font.MeasureString(label);
Vector2 textPos = iconPos + new Vector2(sprite.size.X * symbolScale * 0.7f * Math.Sign(-iconDiff.X), -textSize.Y / 2);
if (iconDiff.X > 0) { textPos.X -= textSize.X; }
DrawString(spriteBatch, textPos + Vector2.One, label, Color.Black);
DrawString(spriteBatch, textPos, label, color);
}
}
if (screenDist - 10 > iconDiff.Length())
{
Vector2 normalizedDiff = Vector2.Normalize(targetScreenPos - iconPos);
Vector2 arrowOffset = normalizedDiff * sprite.size.X * symbolScale * 0.7f;
@@ -1465,9 +1487,9 @@ namespace Barotrauma
depth);
}
public static void DrawString(SpriteBatch sb, Vector2 pos, LocalizedString text, Color color, Color? backgroundColor = null, int backgroundPadding = 0, GUIFont font = null)
public static void DrawString(SpriteBatch sb, Vector2 pos, LocalizedString text, Color color, Color? backgroundColor = null, int backgroundPadding = 0, GUIFont font = null, ForceUpperCase forceUpperCase = ForceUpperCase.Inherit)
{
DrawString(sb, pos, text.Value, color, backgroundColor, backgroundPadding, font);
DrawString(sb, pos, text.Value, color, backgroundColor, backgroundPadding, font, forceUpperCase);
}
public static void DrawString(SpriteBatch sb, Vector2 pos, string text, Color color, Color? backgroundColor = null, int backgroundPadding = 0, GUIFont font = null, ForceUpperCase forceUpperCase = ForceUpperCase.Inherit)

View File

@@ -486,7 +486,7 @@ namespace Barotrauma
{
if (bounceTimer > 3.0f || bounceDown)
{
RectTransform.ScreenSpaceOffset = new Point(RectTransform.ScreenSpaceOffset.X, (int) -(bounceJump * 10f));
RectTransform.ScreenSpaceOffset = new Point(RectTransform.ScreenSpaceOffset.X, (int) -(bounceJump * 15f * GUI.Scale));
if (!bounceDown)
{
bounceJump += deltaTime * 4;
@@ -503,6 +503,7 @@ namespace Barotrauma
bounceJump = 0.0f;
bounceTimer = 0.0f;
bounceDown = false;
Bounce = false;
}
}
}
@@ -730,7 +731,7 @@ namespace Barotrauma
public void DrawToolTip(SpriteBatch spriteBatch)
{
if (!Visible) { return; }
DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect);
DrawToolTip(spriteBatch, ToolTip, Rect);
}
public static void DrawToolTip(SpriteBatch spriteBatch, RichString toolTip, Vector2 pos)
@@ -781,7 +782,7 @@ namespace Barotrauma
if (toolTipBlock.Rect.Bottom > GameMain.GraphicsHeight - 10)
{
toolTipBlock.RectTransform.AbsoluteOffset -= new Point(
(targetElement.Width / 2) * Math.Sign(targetElement.Center.X - toolTipBlock.Center.X),
0,
toolTipBlock.Rect.Bottom - (GameMain.GraphicsHeight - 10));
}
toolTipBlock.SetTextPos();
@@ -806,9 +807,16 @@ namespace Barotrauma
flashColor = (color == null) ? GUIStyle.Red : (Color)color;
}
public void FadeOut(float duration, bool removeAfter, float wait = 0.0f)
public void ImmediateFlash(Color? color = null)
{
CoroutineManager.StartCoroutine(LerpAlpha(0.0f, duration, removeAfter, wait));
flashTimer = MathHelper.Pi / 4.0f * 0.1f;
flashDuration = 1.0f *0.1f;
flashColor = (color == null) ? GUIStyle.Red : (Color)color;
}
public void FadeOut(float duration, bool removeAfter, float wait = 0.0f, Action onRemove = null)
{
CoroutineManager.StartCoroutine(LerpAlpha(0.0f, duration, removeAfter, wait, onRemove));
}
public void FadeIn(float wait, float duration)
@@ -870,7 +878,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
private IEnumerable<CoroutineStatus> LerpAlpha(float to, float duration, bool removeAfter, float wait = 0.0f)
private IEnumerable<CoroutineStatus> LerpAlpha(float to, float duration, bool removeAfter, float wait = 0.0f, Action onRemove = null)
{
State = ComponentState.None;
float t = 0.0f;
@@ -895,6 +903,7 @@ namespace Barotrauma
if (removeAfter && Parent != null)
{
Parent.RemoveChild(this);
onRemove?.Invoke();
}
yield return CoroutineStatus.Success;
@@ -1156,7 +1165,7 @@ namespace Barotrauma
try
{
#if USE_STEAM
Steam.SteamManager.OverlayCustomURL(url);
Steam.SteamManager.OverlayCustomUrl(url);
#else
ToolBox.OpenFileWithShell(url);
#endif

View File

@@ -62,7 +62,7 @@ namespace Barotrauma
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);
int horizontalPadding = (int)(padding.X + padding.Z), verticalPadding = (int)(padding.Y + padding.W);
bool hasHeader = !header.IsNullOrWhiteSpace();
//----------------------------------------------------------------------------------
@@ -131,9 +131,15 @@ namespace Barotrauma
optionElement.ToolTip = option.Tooltip;
}
if (!option.IsEnabled)
//option doesn't do anything, make it a label
if (option.OnSelected == null)
{
optionElement.TextColor *= 0.5f;
optionElement.TextAlignment = Alignment.BottomLeft;
optionElement.TextColor = optionElement.DisabledTextColor = GUIStyle.Green;
}
else if (!option.IsEnabled)
{
optionElement.TextColor *= 0.5f;
}
}
@@ -146,7 +152,10 @@ namespace Barotrauma
// Resize all children to the size of their text
foreach (GUITextBlock block in children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
{
block.RectTransform.NonScaledSize = new Point((int) (block.TextSize.X + (block.Padding.X + block.Padding.Z)), (int) (18 * GUI.Scale));
bool isLabel = block.UserData is ContextMenuOption option && option.OnSelected == null;
block.RectTransform.NonScaledSize = new Point(
(int)(block.TextSize.X + (block.Padding.X + block.Padding.Z)),
(int)Math.Max(block.TextSize.Y * 1.2f, 18 * GUI.Scale));
}
int largestWidth = children.Max(c => c.Rect.Width + horizontalPadding);
@@ -155,7 +164,7 @@ namespace Barotrauma
if (HeaderLabel != null)
{
RectTransform headerTransform = HeaderLabel.RectTransform;
headerTransform.MinSize = new Point((int) (HeaderLabel.TextSize.X + (headerPadding.X + headerPadding.Z)), headerTransform.NonScaledSize.Y);
headerTransform.MinSize = new Point((int)(HeaderLabel.TextSize.X + (headerPadding.X + headerPadding.Z)), headerTransform.NonScaledSize.Y);
if (largestWidth < headerTransform.MinSize.X)
{
largestWidth = headerTransform.MinSize.X;
@@ -171,7 +180,7 @@ namespace Barotrauma
// the cropped size of the option list
Point newSize = new Point(largestWidth, children.Sum(c => c.Rect.Height) + verticalPadding);
// resize the menu itself taking into account the option menus relative Y size
RectTransform.NonScaledSize = new Point(newSize.X, (int) (newSize.Y / optionList.RectTransform.RelativeSize.Y));
RectTransform.NonScaledSize = new Point(newSize.X, (int)(newSize.Y / optionList.RectTransform.RelativeSize.Y));
optionList.RectTransform.NonScaledSize = newSize;
// move the context menu if it would go outside of screen
@@ -227,8 +236,8 @@ namespace Barotrauma
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);
size.Y += (int) Math.Ceiling(textSize.Y);
size.X = Math.Max((int)Math.Ceiling(textSize.X), size.X);
size.Y += (int)Math.Ceiling(textSize.Y);
return textSize;
}

View File

@@ -0,0 +1,97 @@
using Microsoft.Xna.Framework;
using System;
namespace Barotrauma
{
public class GUIDragHandle : GUIComponent
{
private readonly RectTransform elementToMove;
private Point originalOffset;
private Vector2 dragStart;
private bool dragStarted;
public Rectangle DragArea;
public Func<RectTransform, bool> ValidatePosition;
public bool Dragging => dragStarted;
public GUIDragHandle(RectTransform rectT, RectTransform elementToMove, string style = "GUIDragIndicator")
: base(style, rectT)
{
enabled = true;
this.elementToMove = elementToMove;
DragArea = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
protected override void Update(float deltaTime)
{
if (!Visible) { return; }
base.Update(deltaTime);
if (enabled)
{
if (dragStarted)
{
Point moveAmount = (PlayerInput.MousePosition - dragStart).ToPoint() - elementToMove.ScreenSpaceOffset;
Rectangle rect = elementToMove.Rect;
rect.Location += moveAmount;
moveAmount.X += Math.Max(DragArea.X - rect.X, 0);
moveAmount.X -= Math.Max(rect.Right - DragArea.Right, 0);
moveAmount.Y += Math.Max(DragArea.Y - rect.Y, 0);
moveAmount.Y -= Math.Max(rect.Bottom - DragArea.Bottom, 0);
if (moveAmount != Point.Zero)
{
elementToMove.ScreenSpaceOffset += moveAmount;
}
bool isPositionValid = ValidatePosition == null || ValidatePosition.Invoke(elementToMove);
if (!PlayerInput.PrimaryMouseButtonHeld())
{
if (!isPositionValid)
{
elementToMove.ScreenSpaceOffset = originalOffset;
elementToMove.GUIComponent?.Flash();
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
}
dragStarted = false;
}
}
else if (Rect.Contains(PlayerInput.MousePosition) && CanBeFocused && Enabled && GUI.IsMouseOn(this) && !(GUI.MouseOn is GUIButton))
{
State = Selected ? ComponentState.HoverSelected : ComponentState.Hover;
if (PlayerInput.PrimaryMouseButtonDown())
{
originalOffset = elementToMove.ScreenSpaceOffset;
dragStart = PlayerInput.MousePosition - elementToMove.ScreenSpaceOffset.ToVector2();
dragStarted = true;
}
}
else
{
if (!ExternalHighlight)
{
State = Selected ? ComponentState.Selected : ComponentState.None;
}
else
{
State = ComponentState.Hover;
}
}
}
foreach (GUIComponent child in Children)
{
//allow buttons to handle their states themselves
if (child is GUIButton) { continue; }
child.State = State;
child.Enabled = enabled;
}
}
}
}

View File

@@ -204,15 +204,7 @@ namespace Barotrauma
currentHighestParent = FindHighestParent();
currentHighestParent.GUIComponent.OnAddedToGUIUpdateList += AddListBoxToGUIUpdateList;
rectT.ParentChanged += (RectTransform newParent) =>
{
currentHighestParent.GUIComponent.OnAddedToGUIUpdateList -= AddListBoxToGUIUpdateList;
if (newParent != null)
{
currentHighestParent = FindHighestParent();
currentHighestParent.GUIComponent.OnAddedToGUIUpdateList += AddListBoxToGUIUpdateList;
}
};
rectT.ParentChanged += _ => RefreshListBoxParent();
}
@@ -396,6 +388,15 @@ namespace Barotrauma
return true;
}
public void RefreshListBoxParent()
{
currentHighestParent.GUIComponent.OnAddedToGUIUpdateList -= AddListBoxToGUIUpdateList;
if (RectTransform.Parent == null) { return; }
currentHighestParent = FindHighestParent();
currentHighestParent.GUIComponent.OnAddedToGUIUpdateList += AddListBoxToGUIUpdateList;
}
private void AddListBoxToGUIUpdateList(GUIComponent parent)
{
//the parent is not our parent anymore :(
@@ -403,11 +404,13 @@ namespace Barotrauma
//and somewhere between this component and the higher parent a component was removed
for (int i = 1; i < parentHierarchy.Count; i++)
{
if (!parentHierarchy[i].IsParentOf(parentHierarchy[i - 1], recursive: false))
if (parentHierarchy[i].IsParentOf(parentHierarchy[i - 1], recursive: false))
{
parent.OnAddedToGUIUpdateList -= AddListBoxToGUIUpdateList;
return;
continue;
}
parent.OnAddedToGUIUpdateList -= AddListBoxToGUIUpdateList;
return;
}
if (Dropped)

View File

@@ -85,11 +85,11 @@ namespace Barotrauma
get { return sprite; }
set
{
if (sprite == value) return;
if (sprite == value) { return; }
sprite = value;
sourceRect = value == null ? Rectangle.Empty : value.SourceRect;
origin = value == null ? Vector2.Zero : value.size / 2;
if (scaleToFit) RecalculateScale();
if (scaleToFit) { RecalculateScale(); }
}
}

View File

@@ -49,7 +49,7 @@ namespace Barotrauma
//scaling the bar linearly with the resolution tends to make them too large on large resolutions
float desiredSize = 25.0f;
float scaledSize = desiredSize * GUI.Scale;
return (int)((desiredSize + scaledSize) / 2.0f);
return (int)Math.Min((desiredSize + scaledSize) / 2.0f, Rect.Height / 3);
}
}
@@ -57,7 +57,8 @@ namespace Barotrauma
{
SelectSingle,
SelectMultiple,
RequireShiftToSelectMultiple
RequireShiftToSelectMultiple,
None
}
public SelectMode CurrentSelectMode = SelectMode.SelectSingle;
@@ -73,6 +74,8 @@ namespace Barotrauma
public bool HideChildrenOutsideFrame = true;
public bool ResizeContentToMakeSpaceForScrollBar = true;
private bool useGridLayout;
private GUIComponent scrollToElement;
@@ -419,7 +422,7 @@ namespace Barotrauma
{
dimensionsNeedsRecalculation = false;
ContentBackground.RectTransform.Resize(Rect.Size);
bool reduceScrollbarSize = KeepSpaceForScrollBar ? ScrollBarEnabled : ScrollBarVisible;
bool reduceScrollbarSize = ResizeContentToMakeSpaceForScrollBar && (KeepSpaceForScrollBar ? ScrollBarEnabled : ScrollBarVisible);
Point contentSize = reduceScrollbarSize ? CalculateFrameSize(ScrollBar.IsHorizontal, ScrollBarSize) : Rect.Size;
Content.RectTransform.Resize(new Point((int)(contentSize.X - Padding.X - Padding.Z), (int)(contentSize.Y - Padding.Y - Padding.W)));
if (!IsScrollBarOnDefaultSide) { Content.RectTransform.SetPosition(Anchor.BottomRight); }
@@ -598,16 +601,13 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
float t = 0.0f;
float startScroll = BarScroll * BarSize;
float startScroll = BarScroll;
float distanceToTravel = ScrollBar.MaxValue - startScroll;
float progress = startScroll;
float speed = distanceToTravel / duration;
while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, progress))
while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, BarScroll))
{
t += CoroutineManager.DeltaTime;
progress += speed * CoroutineManager.DeltaTime;
BarScroll = progress;
BarScroll += speed * CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
@@ -1056,7 +1056,7 @@ namespace Barotrauma
public void Select(int childIndex, Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled, TakeKeyBoardFocus takeKeyBoardFocus = TakeKeyBoardFocus.No, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
if (childIndex >= Content.CountChildren || childIndex < 0) { return; }
if (childIndex >= Content.CountChildren || childIndex < 0 || CurrentSelectMode == SelectMode.None) { return; }
GUIComponent child = Content.GetChild(childIndex);
if (child is null) { return; }
@@ -1155,6 +1155,7 @@ namespace Barotrauma
public void Select(IEnumerable<GUIComponent> children)
{
if (CurrentSelectMode == SelectMode.None) { return; }
Selected = true;
selected.Clear();
selected.AddRange(children.Where(c => Content.Children.Contains(c)));

View File

@@ -1,9 +1,7 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Networking;
using Barotrauma.Extensions;
namespace Barotrauma
@@ -25,15 +23,18 @@ namespace Barotrauma
Default,
InGame,
Vote,
Hint
Hint,
Tutorial
}
private bool IsAnimated => type == Type.InGame || type == Type.Hint || type == Type.Tutorial;
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
public GUILayoutGroup Content { get; private set; }
public GUIFrame InnerFrame { get; private set; }
public GUITextBlock Header { get; private set; }
public GUITextBlock Text { get; private set; }
public string Tag { get; private set; }
public Identifier Tag { get; private set; }
public bool Closed { get; private set; }
public bool DisplayInLoadingScreens;
@@ -60,6 +61,9 @@ namespace Barotrauma
public GUIImage BackgroundIcon { get; private set; }
private GUIImage newBackgroundIcon;
/// <summary>
/// Close the message box automatically after enough time has passed (<see cref="inGameCloseTime"/>)
/// </summary>
public bool AutoClose;
private float openState;
@@ -69,6 +73,13 @@ namespace Barotrauma
private readonly Type type;
/// <summary>
/// Close the message box automatically if the condition is met
/// </summary>
private readonly Func<bool> autoCloseCondition;
public bool FlashOnAutoCloseCondition { get; set; }
public Type MessageBoxType => type;
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
@@ -79,7 +90,9 @@ namespace Barotrauma
this.Buttons[0].OnClicked = Close;
}
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)
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, Func<bool> autoCloseCondition = null, bool hideCloseButton = false)
: 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
@@ -116,6 +129,7 @@ namespace Barotrauma
Anchor anchor = type switch
{
Type.InGame => Anchor.TopCenter,
Type.Tutorial => Anchor.TopCenter,
Type.Hint => Anchor.TopRight,
Type.Vote => Anchor.TopRight,
_ => Anchor.Center
@@ -130,7 +144,7 @@ namespace Barotrauma
}
GUIStyle.Apply(InnerFrame, "", this);
this.type = type;
Tag = tag;
Tag = tag.ToIdentifier();
#warning TODO: These should be broken into separate methods at least
if (type == Type.Default || type == Type.Vote)
@@ -187,12 +201,13 @@ namespace Barotrauma
var button = new GUIButton(new RectTransform(new Vector2(0.6f, 1.0f / buttons.Length), buttonContainer.RectTransform), buttons[i]);
Buttons.Add(button);
}
GUITextBlock.AutoScaleAndNormalize(Buttons.Select(btn => btn.TextBlock));
}
else if (type == Type.InGame)
else if (type == Type.InGame || type == Type.Tutorial)
{
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
CanBeFocused = false;
AutoClose = true;
AutoClose = type == Type.InGame;
GUIStyle.Apply(InnerFrame, "", this);
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center),
@@ -212,37 +227,43 @@ namespace Barotrauma
Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.65f : 0.85f, 1.0f), horizontalLayoutGroup.RectTransform));
var buttonContainer = new GUIFrame(new RectTransform(new Vector2(0.15f, 1.0f), horizontalLayoutGroup.RectTransform), style: null);
Buttons = new List<GUIButton>(1)
if (!hideCloseButton)
{
new GUIButton(new RectTransform(new Vector2(0.3f, 0.5f), buttonContainer.RectTransform, Anchor.Center),
style: "UIToggleButton")
var buttonContainer = new GUIFrame(new RectTransform(new Vector2(0.15f, 1.0f), horizontalLayoutGroup.RectTransform), style: null);
Buttons = new List<GUIButton>(1)
{
OnClicked = Close
}
};
InputType? closeInput = null;
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Use].MouseButton == MouseButton.None)
{
closeInput = InputType.Use;
}
else if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select].MouseButton == MouseButton.None)
{
closeInput = InputType.Select;
}
if (closeInput.HasValue)
{
Buttons[0].ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("Close")} ([InputType.{closeInput.Value}])");
Buttons[0].OnAddedToGUIUpdateList += (GUIComponent component) =>
{
if (!closing && openState >= 1.0f && PlayerInput.KeyHit(closeInput.Value))
new GUIButton(new RectTransform(new Vector2(0.3f, 0.5f), buttonContainer.RectTransform, Anchor.Center),
style: "UIToggleButton")
{
GUIButton btn = component as GUIButton;
btn?.OnClicked(btn, btn.UserData);
btn?.Flash(GUIStyle.Green);
OnClicked = Close
}
};
InputType? closeInput = null;
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Use].MouseButton == MouseButton.None)
{
closeInput = InputType.Use;
}
else if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select].MouseButton == MouseButton.None)
{
closeInput = InputType.Select;
}
if (closeInput.HasValue)
{
Buttons[0].ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("Close")} ([InputType.{closeInput.Value}])");
Buttons[0].OnAddedToGUIUpdateList += (GUIComponent component) =>
{
if (!closing && openState >= 1.0f && PlayerInput.KeyHit(closeInput.Value))
{
GUIButton btn = component as GUIButton;
btn?.OnClicked(btn, btn.UserData);
btn?.Flash(GUIStyle.Green);
}
};
}
}
else
{
Buttons.Clear();
}
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
@@ -274,7 +295,10 @@ namespace Barotrauma
Content.RectTransform.NonScaledSize =
new Point(Content.Rect.Width, height);
}
Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y);
if (!hideCloseButton)
{
Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y);
}
}
else if (type == Type.Hint)
{
@@ -408,6 +432,8 @@ namespace Barotrauma
}
}
this.autoCloseCondition = autoCloseCondition;
MessageBoxes.Add(this);
}
@@ -448,7 +474,7 @@ namespace Barotrauma
{
// Message box not of type GUIMessageBox is likely the round summary
MessageBoxes[i].AddToGUIUpdateList();
break;
if (!(MessageBoxes[i].UserData is RoundSummary)) { break; }
}
continue;
}
@@ -471,6 +497,7 @@ namespace Barotrauma
public void SetBackgroundIcon(Sprite icon)
{
if (icon == null) { return; }
if (icon == BackgroundIcon?.Sprite) { return; }
GUIImage newIcon = new GUIImage(new RectTransform(icon.size.ToPoint(), RectTransform), icon)
{
IgnoreLayoutGroups = true,
@@ -510,10 +537,10 @@ namespace Barotrauma
}
}
if (type == Type.InGame || type == Type.Hint)
if (IsAnimated)
{
Vector2 initialPos, defaultPos, endPos;
if (type == Type.InGame)
if (type == Type.InGame || type == Type.Tutorial)
{
initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
@@ -552,6 +579,14 @@ namespace Barotrauma
{
Close();
}
else if (autoCloseCondition != null && autoCloseCondition())
{
Close();
if (FlashOnAutoCloseCondition)
{
InnerFrame.Flash(GUIStyle.Green);
}
}
}
else
{
@@ -593,7 +628,7 @@ namespace Barotrauma
{
newBackgroundIcon.SetAsFirstChild();
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
newBackgroundIcon.Color = Color.Lerp(Color.Transparent, Color.White, iconState);
if (newBackgroundIcon.Color.A == 255)
{
BackgroundIcon = newBackgroundIcon;
@@ -608,10 +643,9 @@ namespace Barotrauma
}
}
public void Close()
{
if (type == Type.InGame || type == Type.Hint)
if (IsAnimated)
{
closing = true;
}
@@ -636,6 +670,19 @@ namespace Barotrauma
MessageBoxes.Clear();
}
public static void Close(Identifier tag)
{
foreach (var messageBox in MessageBoxes)
{
if (messageBox is GUIMessageBox mb && mb.Tag == tag)
{
mb.Close();
}
}
}
public static void Close(string tag) => Close(tag.ToIdentifier());
/// <summary>
/// Parent does not matter. It's overridden.
/// </summary>

View File

@@ -178,19 +178,19 @@ namespace Barotrauma
{
public GUIFont(string identifier) : base(identifier) { }
public bool HasValue => Prefabs.Any();
public bool HasValue => !Prefabs.IsEmpty;
public ScalableFont Value => Prefabs.ActivePrefab.Font;
public static implicit operator ScalableFont(GUIFont reference) => reference.Value;
public bool ForceUpperCase => HasValue && Value.ForceUpperCase;
public bool ForceUpperCase => Prefabs.ActivePrefab?.Font is { ForceUpperCase: true };
public uint Size => HasValue ? Value.Size : 0;
private ScalableFont GetFontForStr(LocalizedString str) => GetFontForStr(str.Value);
private ScalableFont GetFontForStr(string str) =>
public 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)

View File

@@ -141,11 +141,32 @@ namespace Barotrauma
public readonly static GUIColor HealthBarColorMedium = new GUIColor("HealthBarColorMedium");
public readonly static GUIColor HealthBarColorHigh = new GUIColor("HealthBarColorHigh");
public static Point ItemFrameMargin => new Point(50, 56).Multiply(GUI.SlicedSpriteScale);
public static Point ItemFrameMargin
{
get
{
Point size = new Point(50, 56).Multiply(GUI.SlicedSpriteScale);
var style = GetComponentStyle("ItemUI");
var sprite = style?.Sprites[GUIComponent.ComponentState.None].First();
if (sprite != null)
{
size.X = Math.Min(sprite.Slices[0].Width + sprite.Slices[2].Width, size.X);
size.Y = Math.Min(sprite.Slices[0].Height + sprite.Slices[6].Height, size.Y);
}
return size;
}
}
public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
public static GUIComponentStyle GetComponentStyle(string name)
=> ComponentStyles.ContainsKey(name) ? ComponentStyles[name] : null;
public static GUIComponentStyle GetComponentStyle(string styleName)
{
return GetComponentStyle(styleName.ToIdentifier());
}
public static GUIComponentStyle GetComponentStyle(Identifier identifier)
=> ComponentStyles.TryGet(identifier, out var style) ? style : null;
public static void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null)
{

View File

@@ -587,7 +587,7 @@ namespace Barotrauma
if (Shadow)
{
Vector2 shadowOffset = new Vector2(GUI.IntScale(2));
Vector2 shadowOffset = new Vector2(Math.Max(GUI.IntScale(2), 1));
Font.DrawString(spriteBatch, textToShow, pos + shadowOffset, Color.Black, 0.0f, origin, TextScale, SpriteEffects.None, textDepth, alignment: textAlignment, forceUpperCase: ForceUpperCase);
}

View File

@@ -412,7 +412,8 @@ namespace Barotrauma
return;
}
if (MouseRect.Contains(PlayerInput.MousePosition) && (GUI.MouseOn == null || (!(GUI.MouseOn is GUIButton) && GUI.IsMouseOn(this))))
bool isMouseOn = MouseRect.Contains(PlayerInput.MousePosition) && (GUI.MouseOn == null || (!(GUI.MouseOn is GUIButton) && GUI.IsMouseOn(this)));
if (isMouseOn || isSelecting)
{
State = ComponentState.Hover;
if (PlayerInput.PrimaryMouseButtonDown())
@@ -448,10 +449,6 @@ namespace Barotrauma
isSelecting = false;
State = ComponentState.None;
}
if (!isSelecting)
{
isSelecting = PlayerInput.IsShiftDown();
}
if (mouseHeldInside && !PlayerInput.PrimaryMouseButtonHeld())
{

View File

@@ -177,7 +177,7 @@ namespace Barotrauma
radioButtonGroup = rbg;
}
private void ResizeBox()
public void ResizeBox()
{
Vector2 textBlockScale = new Vector2(Math.Max(Rect.Width - box.Rect.Width, 0.0f) / Math.Max(Rect.Width, 1.0f), 1.0f);
text.RectTransform.RelativeSize = textBlockScale;

View File

@@ -25,6 +25,10 @@ namespace Barotrauma
{
get; private set;
}
public static Rectangle TutorialObjectiveListArea
{
get; private set;
}
public static Rectangle MessageAreaTop
{
@@ -81,6 +85,11 @@ namespace Barotrauma
get; private set;
}
public static Rectangle ItemHUDArea
{
get; private set;
}
public static int Padding
{
get; private set;
@@ -162,25 +171,35 @@ namespace Barotrauma
HealthWindowAreaLeft = new Rectangle(healthWindowX, healthWindowY, healthWindowWidth, healthWindowHeight);
int objectiveListAreaX = HealthWindowAreaLeft.Right + Padding;
int objectiveListAreaY = ButtonAreaTop.Bottom + Padding;
TutorialObjectiveListArea = new Rectangle(objectiveListAreaX, objectiveListAreaY, (GameMain.GraphicsWidth - Padding) - objectiveListAreaX, (AfflictionAreaLeft.Top - Padding) - objectiveListAreaY);
int votingAreaWidth = (int)(400 * GUI.Scale);
int votingAreaX = GameMain.GraphicsWidth - Padding - votingAreaWidth;
int votingAreaY = Padding + ButtonAreaTop.Height;
// Height is based on text content
VotingArea = new Rectangle(votingAreaX, votingAreaY, votingAreaWidth, 0);
ItemHUDArea = new Rectangle(0, ButtonAreaTop.Bottom, GameMain.GraphicsWidth, GameMain.GraphicsHeight - ButtonAreaTop.Bottom - InventoryAreaLower.Height);
}
public static void Draw(SpriteBatch spriteBatch)
{
GUI.DrawRectangle(spriteBatch, ButtonAreaTop, Color.White * 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);
GUI.DrawRectangle(spriteBatch, AfflictionAreaLeft, Color.Red * 0.5f);
GUI.DrawRectangle(spriteBatch, InventoryAreaLower, Color.Yellow * 0.5f);
GUI.DrawRectangle(spriteBatch, HealthWindowAreaLeft, Color.Red * 0.5f);
GUI.DrawRectangle(spriteBatch, BottomRightInfoArea, Color.Green * 0.5f);
DrawRectangle(ButtonAreaTop, Color.White * 0.5f);
DrawRectangle(TutorialObjectiveListArea, GUIStyle.Blue * 0.5f);
DrawRectangle(MessageAreaTop, GUIStyle.Orange * 0.5f);
DrawRectangle(CrewArea, Color.Blue * 0.5f);
DrawRectangle(ChatBoxArea, Color.Cyan * 0.5f);
DrawRectangle(HealthBarArea, Color.Red * 0.5f);
DrawRectangle(AfflictionAreaLeft, Color.Red * 0.5f);
DrawRectangle(InventoryAreaLower, Color.Yellow * 0.5f);
DrawRectangle(HealthWindowAreaLeft, Color.Red * 0.5f);
DrawRectangle(BottomRightInfoArea, Color.Green * 0.5f);
DrawRectangle(ItemHUDArea, Color.Magenta * 0.3f);
void DrawRectangle(Rectangle r, Color c) => GUI.DrawRectangle(spriteBatch, r, c);
}
}

View File

@@ -233,7 +233,9 @@ namespace Barotrauma
get
{
Point absoluteOffset = ConvertOffsetRelativeToAnchor(AbsoluteOffset, Anchor);
Point relativeOffset = NonScaledParentRect.MultiplySize(RelativeOffset);
Point relativeOffset = new Point(
(int)(NonScaledParentSize.X * RelativeOffset.X),
(int)(NonScaledParentSize.Y * RelativeOffset.Y));
relativeOffset = ConvertOffsetRelativeToAnchor(relativeOffset, Anchor);
return AnchorPoint + PivotOffset + absoluteOffset + relativeOffset + ScreenSpaceOffset;
}
@@ -256,6 +258,7 @@ namespace Barotrauma
public Rectangle ParentRect => Parent != null ? Parent.Rect : UIRect;
protected Rectangle NonScaledRect => new Rectangle(NonScaledTopLeft, NonScaledSize);
protected virtual Rectangle NonScaledUIRect => NonScaledRect;
protected Point NonScaledParentSize => parent?.NonScaledSize ?? new Point(GUI.UIWidth, GameMain.GraphicsHeight);
protected Rectangle NonScaledParentRect => parent != null ? Parent.NonScaledRect : UIRect;
protected Rectangle NonScaledParentUIRect => parent != null ? Parent.NonScaledUIRect : UIRect;
protected Rectangle UIRect => new Rectangle(0, 0, GUI.UIWidth, GameMain.GraphicsHeight);
@@ -336,6 +339,11 @@ namespace Barotrauma
public event Action<RectTransform> ChildrenChanged;
public event Action ScaleChanged;
public event Action SizeChanged;
public void ResetSizeChanged()
{
SizeChanged = null;
}
#endregion
#region Initialization
@@ -730,17 +738,17 @@ namespace Barotrauma
get { return animTargetPos ?? AbsoluteOffset; }
}
public void MoveOverTime(Point targetPos, float duration)
public void MoveOverTime(Point targetPos, float duration, Action onDoneMoving = null)
{
animTargetPos = targetPos;
CoroutineManager.StartCoroutine(DoMoveAnimation(targetPos, duration));
CoroutineManager.StartCoroutine(DoMoveAnimation(targetPos, duration, onDoneMoving));
}
public void ScaleOverTime(Point targetSize, float duration)
{
CoroutineManager.StartCoroutine(DoScaleAnimation(targetSize, duration));
}
private IEnumerable<CoroutineStatus> DoMoveAnimation(Point targetPos, float duration)
private IEnumerable<CoroutineStatus> DoMoveAnimation(Point targetPos, float duration, Action onDoneMoving = null)
{
Vector2 startPos = AbsoluteOffset.ToVector2();
float t = 0.0f;
@@ -752,6 +760,7 @@ namespace Barotrauma
}
AbsoluteOffset = targetPos;
animTargetPos = null;
onDoneMoving?.Invoke();
yield return CoroutineStatus.Success;
}
private IEnumerable<CoroutineStatus> DoScaleAnimation(Point targetSize, float duration)

View File

@@ -238,7 +238,7 @@ namespace Barotrauma
errorId = "Store.SelectStore:StoreDoesntExist";
msg = $"Error selecting store with identifier \"{identifier}\" at {CurrentLocation}: store with the identifier doesn't exist at the location.";
}
DebugConsole.ShowError(msg);
DebugConsole.LogError(msg);
GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg);
}
}
@@ -263,7 +263,7 @@ namespace Barotrauma
}
if (!msg.IsNullOrEmpty())
{
DebugConsole.ShowError(msg);
DebugConsole.LogError(msg);
GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg);
}
}
@@ -1250,7 +1250,7 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error getting item price: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"Error getting item price: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
}
}
@@ -1808,7 +1808,7 @@ namespace Barotrauma
{
string errorMsg = $"Error creating a store quantity label text: unknown store tab.\n{e.StackTrace.CleanupStackTrace()}";
#if DEBUG
DebugConsole.ShowError(errorMsg);
DebugConsole.LogError(errorMsg);
#else
DebugConsole.AddWarning(errorMsg);
#endif
@@ -1882,7 +1882,7 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error getting item availability: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"Error getting item availability: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
}
if (list != null && list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
{
@@ -1962,7 +1962,7 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error adding an item to the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"Error adding an item to the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
}
@@ -1982,7 +1982,7 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error clearing the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"Error clearing the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
}
@@ -2029,7 +2029,7 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error confirming the store transaction: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"Error confirming the store transaction: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
var itemsToRemove = new List<PurchasedItem>();

View File

@@ -56,6 +56,7 @@ namespace Barotrauma
public SubmarineInfo displayedSubmarine;
public GUITextBlock submarineName;
public GUITextBlock submarineClass;
public GUITextBlock submarineTier;
public GUITextBlock submarineFee;
public GUIButton selectSubmarineButton;
public GUITextBlock middleTextBlock;
@@ -162,7 +163,12 @@ namespace Barotrauma
IgnoreLayoutGroups = true
};
new GUIListBox(new RectTransform(Vector2.One, infoFrame.RectTransform)) { IgnoreLayoutGroups = true, CanBeFocused = false };
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) };
specsFrame = new GUIListBox(new RectTransform(new Vector2(0.39f, 1f), infoFrame.RectTransform), style: null)
{
CurrentSelectMode = GUIListBox.SelectMode.None,
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: GUIStyle.Font, wrap: true) { CanBeFocused = false };
@@ -220,16 +226,18 @@ 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: 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.submarineName = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont);
submarineDisplayElement.submarineFee = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.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")
submarineDisplayElement.previewButton = new GUIButton(new RectTransform(Vector2.One * 0.12f, submarineDisplayElement.background.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point((int)(0.03f * background.Rect.Height)) }, style: "ExpandButton")
{
Color = Color.White,
HoverColor = Color.White,
PressedColor = Color.White
};
submarineDisplayElement.submarineClass = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding + (int)GUIStyle.Font.MeasureString(submarineDisplayElement.submarineName.Text).Y) }, string.Empty, textAlignment: Alignment.Left);
submarineDisplayElement.submarineTier = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding + (int)GUIStyle.Font.MeasureString(submarineDisplayElement.submarineName.Text).Y) }, string.Empty, textAlignment: Alignment.Right);
submarineDisplays[i] = submarineDisplayElement;
}
@@ -355,6 +363,7 @@ namespace Barotrauma
submarineDisplays[i].submarineName.Text = string.Empty;
submarineDisplays[i].submarineFee.Text = string.Empty;
submarineDisplays[i].submarineClass.Text = string.Empty;
submarineDisplays[i].submarineTier.Text = string.Empty;
submarineDisplays[i].selectSubmarineButton.Enabled = false;
submarineDisplays[i].selectSubmarineButton.OnClicked = null;
submarineDisplays[i].displayedSubmarine = null;
@@ -387,8 +396,13 @@ namespace Barotrauma
return true;
};
submarineDisplays[i].submarineName.Text = subToDisplay.DisplayName;
submarineDisplays[i].submarineName.Text = subToDisplay.DisplayName;
submarineDisplays[i].submarineClass.Text = TextManager.GetWithVariable("submarineclass.classsuffixformat", "[type]", TextManager.Get($"submarineclass.{subToDisplay.SubmarineClass}"));
submarineDisplays[i].submarineClass.ToolTip = TextManager.Get("submarineclass.description") + "\n\n" + TextManager.Get($"submarineclass.{subToDisplay.SubmarineClass}.description");
submarineDisplays[i].submarineTier.Text = TextManager.Get($"submarinetier.{subToDisplay.Tier}");
submarineDisplays[i].submarineTier.ToolTip = TextManager.Get("submarinetier.description");
if (!GameMain.GameSession.IsSubmarineOwned(subToDisplay))
{
@@ -847,7 +861,7 @@ namespace Barotrauma
msgBox = new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("purchasesubmarinetext",
("[submarinename]", selectedSubmarine.DisplayName),
("[amount]", selectedSubmarine.Price.ToString()),
("[currencyname]", currencyName)), messageBoxOptions);
("[currencyname]", currencyName)) + '\n' + TextManager.Get("submarineswitchinstruction"), messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{

View File

@@ -206,9 +206,9 @@ namespace Barotrauma
transferMenuButton.RectTransform.AbsoluteOffset = new Point(0, -pos - transferMenu.Rect.Height);
}
GameSession.UpdateTalentNotificationIndicator(talentPointNotification);
if (Character.Controlled is { } controlled && talentResetButton != null && talentApplyButton != null)
if (Character.Controlled?.Info is { } characterInfo && talentResetButton != null && talentApplyButton != null)
{
int talentCount = selectedTalents.Count - controlled.Info.GetUnlockedTalentsInTree().Count();
int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
{
@@ -403,7 +403,7 @@ namespace Barotrauma
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
break;
case InfoFrameTab.Talents:
CreateTalentInfo(infoFrameHolder);
CreateCharacterInfo(infoFrameHolder);
break;
}
}
@@ -631,7 +631,7 @@ namespace Barotrauma
linkedGUIList = new List<LinkedGUI>();
List<Client> connectedClients = GameMain.Client.ConnectedClients;
var connectedClients = GameMain.Client.ConnectedClients;
for (int i = 0; i < teamIDs.Count; i++)
{
@@ -1722,7 +1722,11 @@ namespace Barotrauma
var subInfoTextLayout = new GUILayoutGroup(new RectTransform(Vector2.One, paddedFrame.RectTransform));
LocalizedString className = !sub.Info.HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{sub.Info.SubmarineClass}") : TextManager.Get("shuttle");
LocalizedString className = !sub.Info.HasTag(SubmarineTag.Shuttle) ?
TextManager.GetWithVariables("submarine.classandtier",
("[class]", TextManager.Get($"submarineclass.{sub.Info.SubmarineClass}")),
("[tier]", TextManager.Get($"submarinetier.{sub.Info.Tier}"))) :
TextManager.Get("shuttle");
int nameHeight = (int)GUIStyle.LargeFont.MeasureString(sub.Info.DisplayName, true).Y;
int classHeight = (int)GUIStyle.SubHeadingFont.MeasureString(className).Y;
@@ -1763,7 +1767,10 @@ namespace Barotrauma
}
else
{
var specsListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.57f), paddedFrame.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft));
var specsListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.57f), paddedFrame.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft))
{
CurrentSelectMode = GUIListBox.SelectMode.None
};
sub.Info.CreateSpecsWindow(specsListBox, GUIStyle.Font, includeTitle: false, includeClass: false, includeDescription: true);
}
}
@@ -1803,162 +1810,126 @@ namespace Barotrauma
{ TalentTree.TalentTreeStageState.Highlighted, new Color(50,47,33,255) },
}.ToImmutableDictionary();
private void CreateTalentInfo(GUIFrame infoFrame)
private void CreateCharacterInfo(GUIFrame infoFrame)
{
infoFrame.ClearChildren();
talentButtons.Clear();
talentCornerIcons.Clear();
GUIFrame talentFrameBackground = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = GUI.IntScale(15);
GUIFrame talentFrameContent = new GUIFrame(new RectTransform(new Point(talentFrameBackground.Rect.Width - padding, talentFrameBackground.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
GUIFrame frame = new GUIFrame(new RectTransform(new Point(background.Rect.Width - padding, background.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
GUIFrame paddedTalentFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), talentFrameContent.RectTransform, Anchor.Center), style: null);
GUIFrame talentFrameMain = new GUIFrame(new RectTransform(Vector2.One, paddedTalentFrame.RectTransform), style: null);
GUIFrame content = new GUIFrame(new RectTransform(new Vector2(0.98f), frame.RectTransform, Anchor.Center), style: null);
GUIFrame characterSettingsFrame = null;
GUILayoutGroup characterLayout = null;
if (!(GameMain.NetworkMember is null))
{
characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrameContent.RectTransform), style: null) { Visible = false };
characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, frame.RectTransform), style: null) { Visible = false };
characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform));
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), style: null);
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
}
/*Character controlledCharacter = Character.Controlled;
if (controlledCharacter == null) { return; }
if (controlledCharacter.Info is null)
{
DebugConsole.ThrowError("No character info found for talent UI");
return;
}*/
Character controlledCharacter = Character.Controlled;
CharacterInfo info = controlledCharacter?.Info ?? GameMain.Client?.CharacterInfo;
if (info == null) { return; }
Job job = info.Job;
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), talentFrameMain.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), content.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = GUI.IntScale(5)
AbsoluteSpacing = GUI.IntScale(10),
Stretch = true
};
GUILayoutGroup talentInfoLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), talentFrameLayoutGroup.RectTransform, Anchor.Center), isHorizontal: true);
GUILayoutGroup topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), contentLayout.RectTransform, Anchor.Center), isHorizontal: true);
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), talentInfoLayoutGroup.RectTransform), onDraw: (batch, component) =>
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), topLayout.RectTransform), onDraw: (batch, component) =>
{
float posY = component.Rect.Center.Y - component.Rect.Width / 2;
info.DrawPortrait(batch, new Vector2(component.Rect.X, posY), Vector2.Zero, component.Rect.Width, false, false);
});
GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), talentInfoLayoutGroup.RectTransform)) { RelativeSpacing = 0.05f };
GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), topLayout.RectTransform))
{
AbsoluteSpacing = GUI.IntScale(5),
CanBeFocused = true
};
Vector2 nameSize = GUIStyle.SubHeadingFont.MeasureString(info.Name);
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
nameBlock.RectTransform.NonScaledSize = nameSize.Pad(nameBlock.Padding).ToPoint();
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
if (!info.OmitJobInMenus)
{
nameBlock.TextColor = job.Prefab.UIColor;
Vector2 jobSize = GUIStyle.SmallFont.MeasureString(job.Name);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
jobBlock.RectTransform.NonScaledSize = jobSize.Pad(jobBlock.Padding).ToPoint();
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
}
LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + info.PersonalityTrait.Name.Replace(" ", "")));
LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), info.PersonalityTrait.DisplayName);
Vector2 traitSize = GUIStyle.SmallFont.MeasureString(traitString);
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont);
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
GUIFrame talentsOutsideTreeFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.35f), nameLayout.RectTransform, Anchor.BottomCenter), style: null);
if (!(GameMain.NetworkMember is null))
IEnumerable<TalentPrefab> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
if (talentsOutsideTree.Count() > 0)
{
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.675f, 1f), talentsOutsideTreeFrame.RectTransform, Anchor.TopLeft),
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"))
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), nameLayout.RectTransform), style: null);
GUILayoutGroup extraTalentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), nameLayout.RectTransform), childAnchor: Anchor.TopCenter);
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), extraTalentLayout.RectTransform, anchor: Anchor.Center), TextManager.Get("talentmenu.extratalents"), font: GUIStyle.SubHeadingFont);
talentPointText.RectTransform.MaxSize = new Point(int.MaxValue, (int)talentPointText.TextSize.Y);
var extraTalentList = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.8f), extraTalentLayout.RectTransform, anchor: Anchor.Center), isHorizontal: true)
{
IgnoreLayoutGroups = true
AutoHideScrollBar = false,
ResizeContentToMakeSpaceForScrollBar = false
};
newCharacterBox.TextBlock.AutoScaleHorizontal = true;
extraTalentList.ScrollBar.RectTransform.SetPosition(Anchor.BottomCenter, Pivot.TopCenter);
extraTalentList.RectTransform.MinSize = new Point(0, GUI.IntScale(65));
extraTalentLayout.Recalculate();
extraTalentList.ForceLayoutRecalculation();
newCharacterBox.OnClicked = (button, o) =>
foreach (var extraTalent in talentsOutsideTree)
{
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
var img = new GUIImage(new RectTransform(new Point(extraTalentList.Content.Rect.Height), extraTalentList.Content.RectTransform), sprite: extraTalent.Icon, scaleToFit: true)
{
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
{
newCharacterBox.Text = TextManager.Get("settings");
if (pendingChangesFrame != null)
{
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
}
OpenMenu();
});
return true;
}
OpenMenu();
return true;
void OpenMenu()
{
characterSettingsFrame!.Visible = true;
talentFrameMain.Visible = false;
}
};
if (!(characterLayout is null))
{
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomRight);
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
{
OnClicked = (button, o) =>
{
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
characterSettingsFrame!.Visible = false;
talentFrameMain.Visible = true;
return true;
}
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{extraTalent.DisplayName}‖color:end‖" + "\n\n" + extraTalent.Description),
Color = GUIStyle.Green
};
img.RectTransform.SizeChanged += () =>
{
img.RectTransform.MaxSize = new Point(img.Rect.Height);
};
}
}
IEnumerable<TalentPrefab> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
GUILayoutGroup skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), topLayout.RectTransform), childAnchor: Anchor.TopRight)
{
AbsoluteSpacing = GUI.IntScale(5),
Stretch = true
};
if (talentsOutsideTree.Count() > 0)
{
//TODO: replace with something more generic
GUIImage endocrineIcon = new GUIImage(new RectTransform(new Vector2(0.275f, 1f), talentsOutsideTreeFrame.RectTransform, anchor: Anchor.TopRight, scaleBasis: ScaleBasis.Normal), style: "EndocrineReminderIcon")
{
ToolTip = $"{TextManager.Get("afflictionname.endocrineboost")}\n\n{string.Join(", ", talentsOutsideTree.Select(e => e.DisplayName))}"
};
}
GUILayoutGroup skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), talentInfoLayoutGroup.RectTransform)) { Stretch = true };
LocalizedString skillString = TextManager.Get("skills");
Vector2 skillSize = GUIStyle.SubHeadingFont.MeasureString(skillString);
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(Vector2.One, skillLayout.RectTransform), skillString, font: GUIStyle.SubHeadingFont);
skillBlock.RectTransform.NonScaledSize = skillSize.Pad(skillBlock.Padding).ToPoint();
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillLayout.RectTransform), TextManager.Get("skills"), font: GUIStyle.SubHeadingFont);
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
CreateTalentSkillList(controlledCharacter, info, skillListBox);
CreateSkillList(controlledCharacter, info, skillListBox);
if (controlledCharacter != null)
new GUIFrame(new RectTransform(new Vector2(1f, 1f), contentLayout.RectTransform), style: "HorizontalLine");
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.6f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
if (controlledCharacter == null)
{
talentTreeListBox.Enabled = false;
}
else
{
if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
new GUIFrame(new RectTransform(new Vector2(1f, 1f), talentFrameLayoutGroup.RectTransform), style: "HorizontalLine");
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
selectedTalents = info.GetUnlockedTalentsInTree().ToList();
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
@@ -1994,24 +1965,24 @@ namespace Barotrauma
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
if (subTree.TalentOptionStages.Count <= i) { continue; }
if (subTree.TalentOptionStages.Length <= i) { continue; }
TalentOption talentOption = subTree.TalentOptionStages[i];
GUILayoutGroup talentOptionCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.7f), talentOptionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
foreach (TalentPrefab talent in talentOption.Talents.OrderBy(t => t.Identifier))
foreach (Identifier talentId in talentOption.TalentIdentifiers.OrderBy(t => t))
{
if (!TalentPrefab.TalentPrefabs.TryGet(talentId, out TalentPrefab talent)) { continue; }
GUIFrame talentFrame = new GUIFrame(new RectTransform(Vector2.One, talentOptionLayoutGroup.RectTransform), style: null)
{
CanBeFocused = false
};
GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null);
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null)
{
ToolTip = RichString.Rich(talent.DisplayName + "\n\n" + talent.Description),
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖" + "\n\n" + talent.Description),
UserData = talent.Identifier,
PressedColor = pressedColor,
Enabled = controlledCharacter != null,
@@ -2078,9 +2049,13 @@ namespace Barotrauma
}
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
GUILayoutGroup talentBottomFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true) { RelativeSpacing = 0.01f };
GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true)
{
RelativeSpacing = 0.01f,
Stretch = true
};
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), talentBottomFrame.RectTransform));
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), bottomLayout.RectTransform));
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
@@ -2097,47 +2072,100 @@ namespace Barotrauma
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
{
OnClicked = ResetTalentSelection
};
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
{
OnClicked = ApplyTalentSelection,
};
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
}
if (!(GameMain.NetworkMember is null))
{
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
{
IgnoreLayoutGroups = false
};
newCharacterBox.TextBlock.AutoScaleHorizontal = true;
newCharacterBox.OnClicked = (button, o) =>
{
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
{
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
{
newCharacterBox.Text = TextManager.Get("settings");
if (pendingChangesFrame != null)
{
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
}
OpenMenu();
});
return true;
}
OpenMenu();
return true;
void OpenMenu()
{
characterSettingsFrame!.Visible = true;
content.Visible = false;
}
};
if (!(characterLayout is null))
{
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomCenter);
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
{
OnClicked = (button, o) =>
{
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
characterSettingsFrame!.Visible = false;
content.Visible = true;
return true;
}
};
}
}
UpdateTalentInfo();
}
private void CreateTalentSkillList(Character character, CharacterInfo info, GUIListBox parent)
private void CreateSkillList(Character character, CharacterInfo info, GUIListBox parent)
{
parent.Content.ClearChildren();
List<GUITextBlock> skillNames = new List<GUITextBlock>();
foreach (Skill skill in info.Job.GetSkills())
{
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = false };
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.0f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = true };
var skillName = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}").Fallback(skill.Identifier.Value));
skillNames.Add(skillName);
skillName.RectTransform.MinSize = new Point(0, skillName.Rect.Height);
skillContainer.RectTransform.MinSize = new Point(0, skillName.Rect.Height);
skillNames.Add(new GUITextBlock(new RectTransform(new Vector2(0.7f, 1f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}").Fallback(skill.Identifier.Value)));
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.CenterRight) { Padding = new Vector4(0, 0, 4, 0) };
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.TopRight);
float modifiedSkillLevel = character?.GetSkillLevel(skill.Identifier) ?? skill.Level;
if (!MathUtils.NearlyEqual(MathF.Floor(modifiedSkillLevel), MathF.Floor(skill.Level)))
{
int skillChange = (int)MathF.Floor(modifiedSkillLevel - skill.Level);
//TODO: if/when we upgrade to C# 9, do neater pattern matching here
string stringColor = true switch
string stringColor = skillChange switch
{
true when skillChange > 0 => XMLExtensions.ColorToString(GUIStyle.Green),
true when skillChange < 0 => XMLExtensions.ColorToString(GUIStyle.Red),
_ => XMLExtensions.ColorToString(GUIStyle.TextColorNormal)
> 0 => XMLExtensions.ToStringHex(GUIStyle.Green),
< 0 => XMLExtensions.ToStringHex(GUIStyle.Red),
_ => XMLExtensions.ToStringHex(GUIStyle.TextColorNormal)
};
RichString changeText = RichString.Rich($"(‖color:{stringColor}‖{(skillChange > 0 ? "+" : string.Empty) + skillChange}‖color:end‖)");
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), changeText) { Padding = Vector4.Zero };
}
skillContainer.Recalculate();
//skillContainer.Recalculate();
}
parent.RecalculateChildren();
@@ -2216,7 +2244,7 @@ namespace Barotrauma
talentButton.icon.HoverColor = hoverColor;
}
CreateTalentSkillList(controlledCharacter, controlledCharacter.Info, skillListBox);
CreateSkillList(controlledCharacter, controlledCharacter.Info, skillListBox);
}
private void ApplyTalents(Character controlledCharacter)

View File

@@ -17,7 +17,7 @@ using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement;
namespace Barotrauma
{
internal class UpgradeStore
internal sealed class UpgradeStore
{
public readonly struct CategoryData
{
@@ -432,13 +432,21 @@ namespace Barotrauma
};
Location location = Campaign.Map.CurrentLocation;
int hullRepairCost = location?.GetAdjustedMechanicalCost(CampaignMode.HullRepairCost) ?? CampaignMode.HullRepairCost;
int itemRepairCost = location?.GetAdjustedMechanicalCost(CampaignMode.ItemRepairCost) ?? CampaignMode.ItemRepairCost;
int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(CampaignMode.ShuttleReplaceCost) ?? CampaignMode.ShuttleReplaceCost;
int hullRepairCost = Campaign.GetHullRepairCost();
int itemRepairCost = Campaign.GetItemRepairCost();
int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost;
if (location != null)
{
hullRepairCost = location.GetAdjustedMechanicalCost(hullRepairCost);
itemRepairCost = location.GetAdjustedMechanicalCost(itemRepairCost);
shuttleRetrieveCost = location.GetAdjustedMechanicalCost(shuttleRetrieveCost);
}
CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallwalls"), "RepairHullButton", hullRepairCost, (button, o) =>
{
if (Campaign.PurchasedHullRepairs)
//cost is zero = nothing to repair
if (Campaign.PurchasedHullRepairs || hullRepairCost <= 0)
{
button.Enabled = false;
return false;
@@ -471,7 +479,7 @@ namespace Barotrauma
return false;
}
return true;
}, Campaign.PurchasedHullRepairs || !HasPermission, isHovered =>
}, Campaign.PurchasedHullRepairs || !HasPermission || hullRepairCost <= 0, isHovered =>
{
highlightWalls = isHovered;
return true;
@@ -479,7 +487,8 @@ namespace Barotrauma
CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallitems"), "RepairItemsButton", itemRepairCost, (button, o) =>
{
if (PlayerBalance >= itemRepairCost && !Campaign.PurchasedItemRepairs)
//cost is zero = nothing to repair
if (PlayerBalance >= itemRepairCost && !Campaign.PurchasedItemRepairs && itemRepairCost > 0)
{
LocalizedString body = TextManager.GetWithVariable("ItemRepairs.PurchasePromptBody", "[amount]", itemRepairCost.ToString());
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
@@ -505,9 +514,8 @@ namespace Barotrauma
button.Enabled = false;
return false;
}
return true;
}, Campaign.PurchasedItemRepairs || !HasPermission, isHovered =>
}, Campaign.PurchasedItemRepairs || !HasPermission || itemRepairCost <= 0, isHovered =>
{
foreach (var (item, itemFrame) in itemPreviews)
{
@@ -839,7 +847,8 @@ namespace Barotrauma
foreach (UpgradePrefab prefab in prefabs)
{
CreateUpgradeEntry(prefab, category, parent.Content, entitiesOnSub);
if (prefab.MaxLevel is 0) { continue; }
CreateUpgradeEntry(prefab, category, parent.Content, submarine, entitiesOnSub);
}
}
@@ -865,7 +874,7 @@ namespace Barotrauma
itemPrefab.SwappableItem.CanBeBought &&
itemPrefab.SwappableItem.SwapIdentifier.Equals(item.Prefab.SwappableItem.SwapIdentifier, StringComparison.OrdinalIgnoreCase)).Cast<ItemPrefab>();
var linkedItems = Campaign.UpgradeManager.GetLinkedItemsToSwap(item) ?? new List<Item>() { item };
var linkedItems = UpgradeManager.GetLinkedItemsToSwap(item) ?? new List<Item>() { item };
//create the swap entry only for one of the items (the one with the smallest ID)
if (linkedItems.Min(it => it.ID) < item.ID) { return; }
@@ -901,7 +910,7 @@ namespace Barotrauma
GUILayoutGroup buttonLayout = new GUILayoutGroup(rectT(1f, 1f, toggleButton.Frame), isHorizontal: true);
LocalizedString slotText = "";
if (linkedItems.Count > 1)
if (linkedItems.Count() > 1)
{
slotText = TextManager.GetWithVariable("weaponslot", "[number]", string.Join(", ", linkedItems.Select(it => (swappableEntities.IndexOf(it) + 1).ToString())));
}
@@ -973,7 +982,7 @@ namespace Barotrauma
bool isPurchased = item.AvailableSwaps.Contains(replacement);
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count;
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count();
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
price, replacement,
@@ -989,7 +998,7 @@ namespace Barotrauma
{
LocalizedString promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody",
("[itemtoinstall]", replacement.Name),
("[amount]", (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count).ToString()));
("[amount]", (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count()).ToString()));
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
{
if (GameMain.NetworkMember != null)
@@ -1033,7 +1042,7 @@ namespace Barotrauma
}
if (toggleButton.Selected)
{
var linkedItems = Campaign.UpgradeManager.GetLinkedItemsToSwap(item);
var linkedItems = UpgradeManager.GetLinkedItemsToSwap(item);
foreach (var itemPreview in itemPreviews)
{
itemPreview.Value.OutlineColor = itemPreview.Value.Color = linkedItems.Contains(itemPreview.Key) ? GUIStyle.Orange : previewWhite;
@@ -1104,7 +1113,7 @@ namespace Barotrauma
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, GUIStyle.Orange);
new GUITextBlock(rectT(0.2f, 1, progressLayout), string.Empty, font: GUIStyle.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
@@ -1116,7 +1125,11 @@ namespace Barotrauma
//negative price = refund
if (price < 0) { formattedPrice = "+" + formattedPrice; }
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
var priceText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center);
var priceText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center)
{
//prices on swappable items are always visible, upgrade prices are enabled in UpdateUpgradeEntry for purchasable upgrades
Visible = userData is ItemPrefab
};
if (price < 0)
{
priceText.TextColor = GUIStyle.Green;
@@ -1175,9 +1188,10 @@ namespace Barotrauma
}
}
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item>? itemsOnSubmarine)
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, Submarine submarine, List<Item>? itemsOnSubmarine)
{
if (Campaign is null) { return; }
Submarine? sub = GameMain.GameSession?.Submarine ?? Submarine.MainSub;
if (Campaign is null || sub is null) { return; }
GUIFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.25f, parent));
var prefabLayout = prefabFrame.GetChild<GUILayoutGroup>();
@@ -1193,7 +1207,7 @@ namespace Barotrauma
var buyButtonLayout = childLayouts[2];
var buyButton = buyButtonLayout.GetChild<GUIButton>();
if (!HasPermission || (itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab))))
if (!HasPermission || !prefab.IsApplicable(submarine.Info) || (itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab))))
{
prefabFrame.Enabled = false;
description.Enabled = false;
@@ -1243,12 +1257,17 @@ namespace Barotrauma
List<Upgrade> upgrades = entity.GetUpgrades();
int upgradesCount = upgrades.Count;
const int maxUpgrades = 4;
itemName.Text = entity is Item ? entity.Name : TextManager.Get("upgradecategory.walls");
Item? item = entity as Item;
itemName.Text = item?.Name ?? TextManager.Get("upgradecategory.walls");
if (slotIndex > -1)
{
itemName.Text = TextManager.GetWithVariables("weaponslotwithname", ("[number]", slotIndex.ToString()), ("[weaponname]", itemName.Text));
}
if (item?.PendingItemSwap != null)
{
itemName.Text = RichString.Rich(itemName.Text + "\n" + TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", item.PendingItemSwap.Name));
}
upgradeList.Content.ClearChildren();
for (var i = 0; i < upgrades.Count && i < maxUpgrades; i++)
{
@@ -1261,7 +1280,7 @@ namespace Barotrauma
// include pending upgrades into the tooltip
foreach (var (prefab, category, level) in upgradeManager.PendingUpgrades)
{
if (entity is Item item && category.CanBeApplied(item, prefab) || entity is Structure && category.IsWallUpgrade)
if (item != null && category.CanBeApplied(item, prefab) || entity is Structure && category.IsWallUpgrade)
{
bool found = false;
foreach (GUITextBlock textBlock in upgradeList.Content.Children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
@@ -1305,6 +1324,8 @@ namespace Barotrauma
Item[] entitiesOnSub = drawnSubmarine.GetItems(true).Where(i => drawnSubmarine.IsEntityFoundOnThisSub(i, true)).ToArray();
foreach (UpgradeCategory category in UpgradeCategory.Categories)
{
//hide categories with no upgrades in them
if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category))) { continue; }
if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
{
yield return category;
@@ -1370,12 +1391,16 @@ namespace Barotrauma
{
if (selectedUpgradeCategoryLayout != null)
{
var linkedItems = HoveredEntity is Item hoveredItem ? Campaign.UpgradeManager.GetLinkedItemsToSwap(hoveredItem) : new List<Item>();
var linkedItems = HoveredEntity is Item hoveredItem ? 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);
}
else
{
ScrollToCategory(data => data.Category.CanBeApplied(item, null));
}
}
}
else
@@ -1436,7 +1461,7 @@ namespace Barotrauma
* |--------------------------------------------------|
* | name |
* |--------------------------------------------------|
* | class |
* | class + tier |
* |--------------------------------------------------|
* | description |
* | |
@@ -1446,13 +1471,24 @@ 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: 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: GUIStyle.Font);
LocalizedString classText = $"{TextManager.GetWithVariable("submarineclass.classsuffixformat", "[type]", TextManager.Get($"submarineclass.{submarine.Info.SubmarineClass}"))}";
// submarine class + tier
new GUITextBlock(rectT(1.0f, 0.15f, submarineInfoFrame), classText, textAlignment: Alignment.Right, font: GUIStyle.Font)
{
ToolTip = TextManager.Get("submarineclass.description") + "\n\n" + TextManager.Get($"submarineclass.{submarine.Info.SubmarineClass}.description")
};
new GUITextBlock(rectT(1.0f, 0.15f, submarineInfoFrame), TextManager.Get($"submarinetier.{submarine.Info.Tier}"), textAlignment: Alignment.Right, font: GUIStyle.Font)
{
ToolTip = TextManager.Get("submarinetier.description")
};
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));
description.Padding = new Vector4(description.Padding.X, 24 * GUI.Scale, description.Padding.Z, description.Padding.W);
List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs) where category.CanBeApplied(item, null) && item.IsPlayerTeamInteractable select item).Cast<Entity>().ToList();
List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs)
where category.CanBeApplied(item, null) && item.IsPlayerTeamInteractable select item).Cast<Entity>().Distinct().ToList();
List<ushort> ids = GameMain.GameSession.SubmarineInfo?.LeftBehindDockingPortIDs ?? new List<ushort>();
pointsOfInterest.AddRange(submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs).Where(item => ids.Contains(item.ID)));
@@ -1600,6 +1636,7 @@ namespace Barotrauma
List<GUITextBlock> textBlocks = buttonParent.GetAllChildren<GUITextBlock>().ToList();
GUITextBlock priceLabel = textBlocks[0];
priceLabel.Visible = true;
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
if (priceLabel != null && !WaitForServerUpdate)
@@ -1638,9 +1675,15 @@ namespace Barotrauma
IEnumerable<UpgradeCategory> applicableCategories)
{
// Disables the parent and only re-enables if the submarine contains valid items
if (!category.IsWallUpgrade && drawnSubmarine != null)
if (!category.IsWallUpgrade && drawnSubmarine?.Info != null)
{
if (applicableCategories.Contains(category))
if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category) && p.GetMaxLevel(drawnSubmarine.Info) > 0))
{
parent.ToolTip = TextManager.Get("upgradecategorynotapplicable");
parent.Enabled = false;
parent.SelectedColor = GUIStyle.Red * 0.5f;
}
else if (applicableCategories.Contains(category))
{
parent.Enabled = true;
parent.SelectedColor = parent.Style.SelectedColor;
@@ -1660,14 +1703,27 @@ namespace Barotrauma
{
if (component.UserData != prefab) { continue; }
if (prefab.MaxLevel is 0)
{
component.Visible = false;
continue;
}
Dictionary<Identifier, GUIComponentStyle> styles = GUIStyle.GetComponentStyle("upgradeindicator").ChildStyles;
if (!styles.ContainsKey("upgradeindicatoron") || !styles.ContainsKey("upgradeindicatordim") || !styles.ContainsKey("upgradeindicatoroff")) { continue; }
GUIComponentStyle onStyle = styles["upgradeindicatoron".ToIdentifier()];
GUIComponentStyle dimStyle = styles["upgradeindicatordim".ToIdentifier()];
GUIComponentStyle offStyle = styles["upgradeindicatoroff".ToIdentifier()];
int maxLevel = prefab.MaxLevel;
if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) >= prefab.MaxLevel)
if (maxLevel == 0)
{
SetOff();
continue;
}
if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) >= maxLevel)
{
// we check this to avoid flickering from re-applying the same style
if (image.Style == onStyle) { continue; }
@@ -1680,25 +1736,42 @@ namespace Barotrauma
}
else
{
if (image.Style == offStyle) { continue; }
SetOff();
}
void SetOff()
{
if (image.Style == offStyle) { return; }
image.ApplyStyle(offStyle);
}
}
}
}
private void ScrollToCategory(Predicate<CategoryData> predicate, GUIListBox.PlaySelectSound playSelectSound = GUIListBox.PlaySelectSound.No)
{
if (currentStoreLayout == null) { return; }
CategoryData? mostAppropriateCategory = null;
GUIComponent? mostAppropriateChild = null;
foreach (GUIComponent child in currentStoreLayout.Content.Children)
{
if (child.UserData is CategoryData data && predicate(data))
{
currentStoreLayout.ScrollToElement(child, playSelectSound);
break;
//choose the category with least items in it as the "most appropriate"
//e.g. when selecting junction boxes, we want to select the "junction boxes" category instead of "electrical repairs" which contains many electrical devices
if (mostAppropriateCategory == null ||
data.Category.ItemTags.Count() < mostAppropriateCategory.Value.Category.ItemTags.Count())
{
mostAppropriateCategory = data;
mostAppropriateChild = child;
}
}
}
if (mostAppropriateChild != null)
{
currentStoreLayout.ScrollToElement(mostAppropriateChild, playSelectSound);
}
}
/// <summary>

View File

@@ -131,9 +131,9 @@ namespace Barotrauma
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)
public void LoadContent(string contentPath, VideoSettings videoSettings, TextSettings textSettings, Identifier contentId, bool startPlayback, LocalizedString objective, Action onStop = null)
{
callbackOnStop = callback;
callbackOnStop = onStop;
filePath = contentPath + videoSettings.File;
if (!File.Exists(filePath))

View File

@@ -66,7 +66,7 @@ namespace Barotrauma
{
currentVoteType = type;
CreateVotingGUI();
if (starter.ID == GameMain.Client.ID) { SetGUIToVotedState(2); }
if (starter.SessionId == GameMain.Client.SessionId) { SetGUIToVotedState(2); }
VoteRunning = true;
}

View File

@@ -111,9 +111,7 @@ namespace Barotrauma
private readonly GameTime fixedTime;
public string ConnectName;
public string ConnectEndpoint;
public UInt64 ConnectLobby;
public Option<ConnectCommand> ConnectCommand = Option<ConnectCommand>.None();
private static SpriteBatch spriteBatch;
@@ -236,20 +234,14 @@ namespace Barotrauma
ConsoleArguments = args;
ConnectName = null;
ConnectEndpoint = null;
ConnectLobby = 0;
try
{
ToolBox.ParseConnectCommand(ConsoleArguments, out ConnectName, out ConnectEndpoint, out ConnectLobby);
ConnectCommand = ToolBox.ParseConnectCommand(ConsoleArguments);
}
catch (IndexOutOfRangeException e)
{
DebugConsole.ThrowError($"Failed to parse console arguments ({string.Join(' ', ConsoleArguments)})", e);
ConnectName = null;
ConnectEndpoint = null;
ConnectLobby = 0;
ConnectCommand = Option<ConnectCommand>.None();
}
GUI.KeyboardDispatcher = new EventInput.KeyboardDispatcher(Window);
@@ -473,7 +465,7 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
UgcTransition.Prepare();
LegacySteamUgcTransition.Prepare();
var contentPackageLoadRoutine = ContentPackageManager.Init();
foreach (var progress in contentPackageLoadRoutine)
{
@@ -569,6 +561,8 @@ namespace Barotrauma
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get(steamError));
}
GameSettings.OnGameMainHasLoaded?.Invoke();
TitleScreen.LoadState = 100.0f;
HasLoaded = true;
if (GameSettings.CurrentConfig.VerboseLogging)
@@ -604,7 +598,7 @@ namespace Barotrauma
{
try
{
ToolBox.ParseConnectCommand(ToolBox.SplitCommand(connectCommand), out ConnectName, out ConnectEndpoint, out ConnectLobby);
ConnectCommand = ToolBox.ParseConnectCommand(ToolBox.SplitCommand(connectCommand));
}
catch (IndexOutOfRangeException e)
{
@@ -613,12 +607,8 @@ namespace Barotrauma
#else
DebugConsole.Log($"Failed to parse a Steam friend's connect invitation command ({connectCommand})\n" + e.StackTrace.CleanupStackTrace());
#endif
ConnectName = null;
ConnectEndpoint = null;
ConnectLobby = 0;
ConnectCommand = Option<ConnectCommand>.None();
}
DebugConsole.NewMessage(ConnectName + ", " + ConnectEndpoint, Color.Yellow);
}
public void OnLobbyJoinRequested(Steamworks.Data.Lobby lobby, Steamworks.SteamId friendId)
@@ -734,7 +724,7 @@ namespace Barotrauma
}
#endif
NetworkMember?.Update((float)Timing.Step);
Client?.Update((float)Timing.Step);
if (!HasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine))
{
@@ -743,38 +733,29 @@ namespace Barotrauma
}
else if (HasLoaded)
{
if (ConnectLobby != 0)
if (ConnectCommand is Some<ConnectCommand> { Value: var connectCommand })
{
if (Client != null)
{
Client.Disconnect();
Client.Quit();
Client = null;
GameMain.MainMenuScreen.Select();
MainMenuScreen.Select();
}
Steam.SteamManager.JoinLobby(ConnectLobby, true);
ConnectLobby = 0;
ConnectEndpoint = null;
ConnectName = null;
}
else if (!string.IsNullOrWhiteSpace(ConnectEndpoint))
{
if (Client != null)
if (connectCommand.EndpointOrLobby.TryGet(out ulong lobbyId))
{
Client.Disconnect();
Client = null;
GameMain.MainMenuScreen.Select();
SteamManager.JoinLobby(lobbyId, joinServer: true);
}
UInt64 serverSteamId = SteamManager.SteamIDStringToUInt64(ConnectEndpoint);
Client = new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(SteamManager.GetUsername()),
serverSteamId != 0 ? null : ConnectEndpoint,
serverSteamId,
string.IsNullOrWhiteSpace(ConnectName) ? ConnectEndpoint : ConnectName);
ConnectLobby = 0;
ConnectEndpoint = null;
ConnectName = null;
else if (connectCommand.EndpointOrLobby.TryGet(out ConnectCommand.NameAndEndpoint nameAndEndpoint)
&& nameAndEndpoint is { ServerName: var serverName, Endpoint: var endpoint })
{
Client = new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(SteamManager.GetUsername()),
endpoint,
string.IsNullOrWhiteSpace(serverName) ? endpoint.StringRepresentation : serverName,
Option<int>.None());
}
ConnectCommand = Option<ConnectCommand>.None();
}
SoundPlayer.Update((float)Timing.Step);
@@ -823,7 +804,7 @@ namespace Barotrauma
else if ((Character.Controlled == null || !itemHudActive())
&& CharacterHealth.OpenHealthWindow == null
&& !CrewManager.IsCommandInterfaceOpen
&& !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedConstruction != null))
&& !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedItem != null))
{
// Otherwise toggle pausing, unless another window/interface is open.
GUI.TogglePauseMenu();
@@ -831,9 +812,9 @@ namespace Barotrauma
static bool itemHudActive()
{
if (Character.Controlled?.SelectedConstruction == null) { return false; }
if (Character.Controlled?.SelectedItem == null) { return false; }
return
Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
Character.Controlled.SelectedItem.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false);
}
}
@@ -902,7 +883,7 @@ namespace Barotrauma
}
}
NetworkMember?.Update((float)Timing.Step);
Client?.Update((float)Timing.Step);
GUI.Update((float)Timing.Step);
@@ -928,7 +909,7 @@ namespace Barotrauma
#endif
}
CoroutineManager.Update((float)Timing.Step, Paused ? 0.0f : (float)Timing.Step);
CoroutineManager.Update(Paused, (float)Timing.Step);
SteamManager.Update((float)Timing.Step);
@@ -1102,7 +1083,7 @@ namespace Barotrauma
if (Client != null)
{
Client.Disconnect();
Client.Quit();
Client = null;
}
@@ -1118,7 +1099,7 @@ namespace Barotrauma
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:CurrentIntensity", GameSession.EventManager.CurrentIntensity);
foreach (var activeEvent in GameSession.EventManager.ActiveEvents)
{
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:ActiveEvents:" + activeEvent.ToString());
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:ActiveEvents:" + activeEvent.Prefab.Identifier);
}
GameSession.LogEndRoundStats(eventId);
if (GameSession.GameMode is TutorialMode tutorialMode)
@@ -1184,7 +1165,7 @@ namespace Barotrauma
UserData = "https://steamcommunity.com/app/602960/discussions/1/",
OnClicked = (btn, userdata) =>
{
if (!SteamManager.OverlayCustomURL(userdata as string))
if (!SteamManager.OverlayCustomUrl(userdata as string))
{
ShowOpenUrlInWebBrowserPrompt(userdata as string);
}
@@ -1222,7 +1203,7 @@ namespace Barotrauma
{
exiting = true;
DebugConsole.NewMessage("Exiting...");
NetworkMember?.Disconnect();
Client?.Quit();
SteamManager.ShutDown();
try

View File

@@ -103,7 +103,7 @@ namespace Barotrauma
{
if (soldItem.ItemPrefab != soldEntity.ItemPrefab) { return false; }
if (matchId && (soldEntity.Item == null || soldItem.ID != soldEntity.Item.ID)) { return false; }
if (soldItem.Origin == SoldItem.SellOrigin.Character && GameMain.Client != null && soldItem.SellerID != GameMain.Client.ID) { return false; }
if (soldItem.Origin == SoldItem.SellOrigin.Character && GameMain.Client != null && soldItem.SellerID != GameMain.Client.SessionId) { return false; }
return true;
}
}
@@ -139,16 +139,16 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error selling items: uknown store tab type \"{sellingMode}\".\n{e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"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;
byte sellerId = GameMain.Client?.SessionId ?? 0;
// 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()}");
DebugConsole.LogError($"Error selling items at {Location}: no store with identifier \"{storeIdentifier}\" exists.\n{Environment.StackTrace.CleanupStackTrace()}");
return;
}
var storeSpecificSoldItems = GetSoldItems(storeIdentifier, create: true);

View File

@@ -183,14 +183,13 @@ namespace Barotrauma
{
chatBox.ToggleButton = new GUIButton(new RectTransform(new Point((int)(182f * GUI.Scale * 0.4f), (int)(99f * GUI.Scale * 0.4f)), chatBox.GUIFrame.Parent.RectTransform), style: "ChatToggleButton")
{
ToolTip = TextManager.Get("chat"),
ToolTip = TextManager.GetWithVariable("hudbutton.chatbox", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ChatBox)),
ClampMouseRectToParent = false
};
chatBox.ToggleButton.RectTransform.AbsoluteOffset = new Point(0, HUDLayoutSettings.ChatBoxArea.Height - chatBox.ToggleButton.Rect.Height);
chatBox.ToggleButton.OnClicked += (GUIButton btn, object userdata) =>
{
chatBox.ToggleOpen = !chatBox.ToggleOpen;
chatBox.CloseAfterMessageSent = false;
chatBox.Toggle();
return true;
};
}
@@ -292,29 +291,13 @@ namespace Barotrauma
return crewArea.Rect;
}
/// <summary>
/// Remove the character from the crew (and crew menus).
/// </summary>
/// <param name="character">The character to remove</param>
/// <param name="removeInfo">If the character info is also removed, the character will not be visible in the round summary.</param>
public void RemoveCharacter(Character character, bool removeInfo = false, bool resetCrewListIndex = true)
{
if (character == null)
{
DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
characters.Remove(character);
if (removeInfo) { characterInfos.Remove(character.Info); }
if (resetCrewListIndex) { ResetCrewListIndex(character); }
}
/// <summary>
/// Add character to the list without actually adding it to the crew
/// </summary>
public GUIComponent AddCharacterToCrewList(Character character)
{
if (character == null) { return null; }
if (crewList.Content.Children.Any(c => c.UserData as Character == character)) { return null; }
var background = new GUIFrame(
new RectTransform(crewListEntrySize, parent: crewList.Content.RectTransform, anchor: Anchor.TopRight),
@@ -510,7 +493,15 @@ namespace Barotrauma
return background;
}
private void SetCharacterComponentTooltip(GUIComponent characterComponent)
public void RemoveCharacterFromCrewList(Character character)
{
if (crewList?.Content.GetChildByUserData(character) is { } component)
{
crewList.RemoveChild(component);
}
}
private static void SetCharacterComponentTooltip(GUIComponent characterComponent)
{
if (!(characterComponent?.UserData is Character character)) { return; }
if (character.Info?.Job?.Prefab == null) { return; }
@@ -1390,7 +1381,7 @@ namespace Barotrauma
}
else
{
CreateCommandUI(HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) ? Character.Controlled : GUI.MouseOn?.UserData as Character);
CreateCommandUI(CharacterHUD.MouseOnCharacterPortrait() ? Character.Controlled : GUI.MouseOn?.UserData as Character);
}
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
clicklessSelectionActive = isOpeningClick = true;
@@ -2412,7 +2403,7 @@ namespace Barotrauma
float reactorOutput = -reactor.CurrPowerConsumption;
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
// --> Create shortcut node for "Operate Reactor" order's "Power Up" option
if (ShouldDelegateOrder("operatereactor") && reactorOutput < float.Epsilon && characters.None(c => c.SelectedConstruction == reactor.Item))
if (ShouldDelegateOrder("operatereactor") && reactorOutput < float.Epsilon && characters.None(c => c.SelectedItem == reactor.Item))
{
var orderPrefab = OrderPrefab.Prefabs["operatereactor"];
var order = new Order(orderPrefab, orderPrefab.Options[0], reactor.Item, reactor);
@@ -2426,7 +2417,7 @@ namespace Barotrauma
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
// --> Create shortcut node for Steer order
if (CanFitMoreNodes() && ShouldDelegateOrder("steer") && IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["steer"]) &&
subItems.Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
subItems.Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedItem == nav) &&
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
{
var order = new Order(OrderPrefab.Prefabs["steer"], steering.Item, steering);
@@ -2822,7 +2813,7 @@ namespace Barotrauma
return node;
}
private struct MinimapNodeData
public struct MinimapNodeData
{
public Order Order;
}
@@ -3517,9 +3508,9 @@ namespace Barotrauma
if (node == null || characterContext != null) { return false; }
if (node.UserData is Order nodeOrder)
{
return !nodeOrder.TargetAllCharacters && !nodeOrder.Prefab.HasOptions &&
(!nodeOrder.MustSetTarget || itemContext != null ||
nodeOrder.GetMatchingItems(GetTargetSubmarine(), true, interactableFor: Character.Controlled).Count < 2);
return !nodeOrder.TargetAllCharacters &&
(!nodeOrder.Prefab.HasOptions || !nodeOrder.Option.IsEmpty) &&
(!nodeOrder.MustSetTarget || itemContext != null || nodeOrder.GetMatchingItems(GetTargetSubmarine(), true, interactableFor: Character.Controlled).Count < 2);
}
return false;
}

View File

@@ -3,8 +3,8 @@ namespace Barotrauma
{
partial class GameMode
{
public virtual void Draw(SpriteBatch spriteBatch)
{
}
public virtual void HUDScaleChanged() { }
public virtual void Draw(SpriteBatch spriteBatch) { }
}
}

View File

@@ -115,12 +115,16 @@ namespace Barotrauma
partial void InitProjSpecific()
{
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUI.Canvas),
isHorizontal: true, childAnchor: Anchor.CenterRight)
{
CanBeFocused = false
};
CreateButtons();
}
public override void HUDScaleChanged()
{
CreateButtons();
}
private void CreateButtons()
{
int buttonHeight = (int) (GUI.Scale * 40),
buttonWidth = GUI.IntScale(450),
buttonCenter = buttonHeight / 2,
@@ -166,8 +170,6 @@ namespace Barotrauma
},
UserData = "ReadyCheckButton"
};
buttonContainer.Recalculate();
}
private void InitCampaignUI()
@@ -311,7 +313,7 @@ namespace Barotrauma
if (prevControlled != null)
{
prevControlled.SelectedConstruction = null;
prevControlled.SelectedItem = prevControlled.SelectedSecondaryItem = null;
if (prevControlled.AIController != null)
{
prevControlled.AIController.Enabled = true;
@@ -362,7 +364,7 @@ namespace Barotrauma
float t = 0.0f;
while (t < fadeOutDuration || endTransition.Running)
{
t += CoroutineManager.UnscaledDeltaTime;
t += CoroutineManager.DeltaTime;
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
yield return CoroutineStatus.Running;
}
@@ -469,7 +471,6 @@ namespace Barotrauma
{
base.End(transitionType);
ForceMapUI = ShowCampaignUI = false;
UpgradeManager.CanUpgrade = true;
// remove all event dialogue boxes
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
@@ -539,37 +540,37 @@ namespace Barotrauma
{
System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
msg.WriteUInt16(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.WriteUInt16(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
var selectedMissionIndices = map.GetSelectedMissionIndices();
msg.Write((byte)selectedMissionIndices.Count());
msg.WriteByte((byte)selectedMissionIndices.Count());
foreach (int selectedMissionIndex in selectedMissionIndices)
{
msg.Write((byte)selectedMissionIndex);
msg.WriteByte((byte)selectedMissionIndex);
}
msg.Write(PurchasedHullRepairs);
msg.Write(PurchasedItemRepairs);
msg.Write(PurchasedLostShuttles);
msg.WriteBoolean(PurchasedHullRepairs);
msg.WriteBoolean(PurchasedItemRepairs);
msg.WriteBoolean(PurchasedLostShuttles);
WriteItems(msg, CargoManager.ItemsInBuyCrate);
WriteItems(msg, CargoManager.ItemsInSellFromSubCrate);
WriteItems(msg, CargoManager.PurchasedItems);
WriteItems(msg, CargoManager.SoldItems);
msg.Write((ushort)UpgradeManager.PurchasedUpgrades.Count);
msg.WriteUInt16((ushort)UpgradeManager.PurchasedUpgrades.Count);
foreach (var (prefab, category, level) in UpgradeManager.PurchasedUpgrades)
{
msg.Write(prefab.Identifier);
msg.Write(category.Identifier);
msg.Write((byte)level);
msg.WriteIdentifier(prefab.Identifier);
msg.WriteIdentifier(category.Identifier);
msg.WriteByte((byte)level);
}
msg.Write((ushort)UpgradeManager.PurchasedItemSwaps.Count);
msg.WriteUInt16((ushort)UpgradeManager.PurchasedItemSwaps.Count);
foreach (var itemSwap in UpgradeManager.PurchasedItemSwaps)
{
msg.Write(itemSwap.ItemToRemove.ID);
msg.Write(itemSwap.ItemToInstall?.Identifier ?? Identifier.Empty);
msg.WriteUInt16(itemSwap.ItemToRemove.ID);
msg.WriteIdentifier(itemSwap.ItemToInstall?.Identifier ?? Identifier.Empty);
}
}
@@ -760,6 +761,7 @@ namespace Barotrauma
item.PendingItemSwap = null;
}
}
campaign.CampaignUI?.UpgradeStore?.RequestRefresh();
}
}

View File

@@ -1,12 +1,8 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -49,6 +45,31 @@ namespace Barotrauma
private bool showCampaignResetText;
public override bool PurchasedHullRepairs
{
get { return PurchasedHullRepairsInLatestSave; }
set
{
PurchasedHullRepairsInLatestSave = value;
}
}
public override bool PurchasedLostShuttles
{
get { return PurchasedLostShuttlesInLatestSave; }
set
{
PurchasedLostShuttlesInLatestSave = value;
}
}
public override bool PurchasedItemRepairs
{
get { return PurchasedItemRepairsInLatestSave; }
set
{
PurchasedItemRepairsInLatestSave = value;
}
}
#region Constructors/initialization
/// <summary>
@@ -132,9 +153,9 @@ namespace Barotrauma
};
}
PurchasedLostShuttles = element.GetAttributeBool("purchasedlostshuttles", false);
PurchasedHullRepairs = element.GetAttributeBool("purchasedhullrepairs", false);
PurchasedItemRepairs = element.GetAttributeBool("purchaseditemrepairs", false);
PurchasedLostShuttlesInLatestSave = element.GetAttributeBool("purchasedlostshuttles", false);
PurchasedHullRepairsInLatestSave = element.GetAttributeBool("purchasedhullrepairs", false);
PurchasedItemRepairsInLatestSave = element.GetAttributeBool("purchaseditemrepairs", false);
CheatsEnabled = element.GetAttributeBool("cheatsenabled", false);
if (CheatsEnabled)
{
@@ -169,10 +190,20 @@ namespace Barotrauma
public static SinglePlayerCampaign Load(XElement element) => new SinglePlayerCampaign(element);
private void InitUI()
{
CreateEndRoundButton();
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
CampaignUI = new CampaignUI(this, campaignUIContainer)
{
StartRound = () => { TryEndRound(); }
};
}
private void CreateEndRoundButton()
{
int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(450);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUI.Canvas),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{
@@ -190,12 +221,11 @@ namespace Barotrauma
return true;
}
};
}
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
CampaignUI = new CampaignUI(this, campaignUIContainer)
{
StartRound = () => { TryEndRound(); }
};
public override void HUDScaleChanged()
{
CreateEndRoundButton();
}
#endregion
@@ -205,7 +235,7 @@ namespace Barotrauma
base.Start();
CargoManager.CreatePurchasedItems();
UpgradeManager.ApplyUpgrades();
UpgradeManager.SanityCheckUpgrades(Submarine.MainSub);
UpgradeManager.SanityCheckUpgrades();
if (!savedOnStart)
{
@@ -292,7 +322,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
timer = Math.Min(timer + CoroutineManager.UnscaledDeltaTime, textDuration);
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
yield return CoroutineStatus.Running;
}
var outpost = GameMain.GameSession.Level.StartOutpost;
@@ -320,7 +350,7 @@ namespace Barotrauma
while (timer < fadeInDuration)
{
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
timer += CoroutineManager.UnscaledDeltaTime;
timer += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
overlayColor = Color.Transparent;
@@ -353,7 +383,7 @@ namespace Barotrauma
if (prevControlled != null)
{
prevControlled.SelectedConstruction = null;
prevControlled.SelectedItem = prevControlled.SelectedSecondaryItem = null;
if (prevControlled.AIController != null)
{
prevControlled.AIController.Enabled = true;
@@ -424,7 +454,7 @@ namespace Barotrauma
float t = 0.0f;
while (t < fadeOutDuration || endTransition.Running)
{
t += CoroutineManager.UnscaledDeltaTime;
t += CoroutineManager.DeltaTime;
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
yield return CoroutineStatus.Running;
}
@@ -436,6 +466,7 @@ namespace Barotrauma
if (success)
{
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
GameMain.GameSession.EventManager.RegisterEventHistory();
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
}
else

View File

@@ -1,346 +0,0 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Tutorials
{
class CaptainTutorial : ScenarioTutorial
{
// Room 1
private float shakeTimer = 1f;
private float shakeAmount = 20f;
// Room 2
private Door captain_firstDoor;
private LightComponent captain_firstDoorLight;
// Room 3
private Character captain_medic;
private MotionSensor captain_medicObjectiveSensor;
private Vector2 captain_medicSpawnPos;
private Door tutorial_submarineDoor;
private LightComponent tutorial_submarineDoorLight;
// Submarine
private MotionSensor captain_enteredSubmarineSensor;
private Steering captain_navConsole;
private Sonar captain_sonar;
private Item captain_statusMonitor;
private Character captain_security;
private Character captain_mechanic;
private Character captain_engineer;
private Reactor tutorial_submarineReactor;
private Door tutorial_lockedDoor_1;
private Door tutorial_lockedDoor_2;
// Variables
private Character captain;
private LocalizedString radioSpeakerName;
private Sprite captain_steerIcon;
private Color captain_steerIconColor;
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)));
}
protected override void Initialize()
{
captain = Character.Controlled;
radioSpeakerName = TextManager.Get("Tutorial.Radio.Watchman");
GameMain.GameSession.CrewManager.AllowCharacterSwitch = false;
foreach (Item item in captain.Inventory.AllItemsMod)
{
if (item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(captain);
captain.Inventory.RemoveItem(item);
}
var steerOrder = OrderPrefab.Prefabs["steer"];
captain_steerIcon = steerOrder.SymbolSprite;
captain_steerIconColor = steerOrder.Color;
// Room 2
captain_firstDoor = Item.ItemList.Find(i => i.HasTag("captain_firstdoor")).GetComponent<Door>();
captain_firstDoorLight = Item.ItemList.Find(i => i.HasTag("captain_firstdoorlight")).GetComponent<LightComponent>();
SetDoorAccess(captain_firstDoor, captain_firstDoorLight, true);
// Room 3
captain_medicObjectiveSensor = Item.ItemList.Find(i => i.HasTag("captain_medicobjectivesensor")).GetComponent<MotionSensor>();
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, jobOrJobPrefab: JobPrefab.Get("medicaldoctor"))
{
TeamID = CharacterTeamType.Team1
};
captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor");
captain_medic.GiveJobItems(null);
captain_medic.CanSpeak = captain_medic.AIController.Enabled = false;
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);
// Submarine
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_sonar = captain_navConsole.Item.GetComponent<Sonar>();
captain_statusMonitor = Item.ItemList.Find(i => i.HasTag("captain_statusmonitor"));
tutorial_submarineReactor.CanBeSelected = false;
tutorial_submarineReactor.IsActive = tutorial_submarineReactor.AutoTemp = false;
tutorial_lockedDoor_1 = Item.ItemList.Find(i => i.HasTag("tutorial_lockeddoor_1")).GetComponent<Door>();
tutorial_lockedDoor_2 = Item.ItemList.Find(i => i.HasTag("tutorial_lockeddoor_2")).GetComponent<Door>();
SetDoorAccess(tutorial_lockedDoor_1, null, false);
SetDoorAccess(tutorial_lockedDoor_2, null, false);
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.GiveJobItems();
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.GiveJobItems();
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.GiveJobItems();
captain_mechanic.CanSpeak = captain_security.CanSpeak = captain_engineer.CanSpeak = false;
captain_mechanic.AIController.Enabled = captain_security.AIController.Enabled = captain_engineer.AIController.Enabled = false;
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Started");
GameAnalyticsManager.AddDesignEvent("Tutorial:Started");
}
public override IEnumerable<CoroutineStatus> UpdateState()
{
while (GameMain.Instance.LoadingScreenOpen) yield return null;
// Room 1
while (shakeTimer > 0.0f) // Wake up, shake
{
shakeTimer -= 0.1f;
GameMain.GameScreen.Cam.Shake = shakeAmount;
yield return new WaitForSeconds(0.1f, false);
}
// Room 2
do { yield return null; } while (!captain_firstDoor.IsOpen);
captain_medic.AIController.Enabled = true;
// Room 3
do { yield return null; } while (!captain_medicObjectiveSensor.MotionDetected);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(captain_medic.Info.DisplayName, TextManager.Get("Captain.Radio.Medic"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
GameMain.GameSession.CrewManager.AutoShowCrewList();
GameMain.GameSession.CrewManager.AddCharacter(captain_medic);
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command));
do
{
yield return null;
// TODO: Rework order highlighting for new command UI
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_medic, "follow", highlightColor, new Vector2(5, 5));
}
while (!HasOrder(captain_medic, "follow"));
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
RemoveCompletedObjective(0);
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Objective0");
// 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, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command));
GameMain.GameSession.CrewManager.AddCharacter(captain_mechanic);
do
{
yield return null;
// TODO: Rework order highlighting for new command UI
// 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(1);
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Objective1");
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(2, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command));
GameMain.GameSession.CrewManager.AddCharacter(captain_security);
do
{
yield return null;
// TODO: Rework order highlighting for new command UI
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_security, "operateweapons", highlightColor, new Vector2(5, 5));
HighlightOrderOption("fireatwill");
}
while (!HasOrder(captain_security, "operateweapons"));
RemoveCompletedObjective(2);
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Objective2");
yield return new WaitForSeconds(4f, false);
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;
// TODO: Rework order highlighting for new command UI
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_engineer, "operatereactor", highlightColor, new Vector2(5, 5));
HighlightOrderOption("powerup");
}
while (!HasOrder(captain_engineer, "operatereactor", "powerup"));
RemoveCompletedObjective(3);
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Objective3");
do { yield return null; } while (!tutorial_submarineReactor.IsActive); // Wait until reactor on
TriggerTutorialSegment(4);
while (ContentRunning) yield return null;
captain.AddActiveObjectiveEntity(captain_navConsole.Item, captain_steerIcon, captain_steerIconColor);
SetHighlight(captain_navConsole.Item, true);
SetHighlight(captain_sonar.Item, true);
SetHighlight(captain_statusMonitor, true);
do
{
//captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f);
yield return new WaitForSeconds(1.0f, false);
} while (Submarine.MainSub.DockedTo.Any());
captain_navConsole.UseAutoDocking = false;
RemoveCompletedObjective(4);
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Objective4");
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(5); // Navigate to destination
do
{
if (IsSelectedItem(captain_navConsole.Item))
{
if (captain_sonar.SonarModeSwitch.Frame.FlashTimer <= 0)
{
captain_sonar.SonarModeSwitch.Frame.Flash(highlightColor, 1.5f, false, false, new Vector2(2.5f, 2.5f));
}
}
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(5);
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Objective5");
captain_navConsole.UseAutoDocking = true;
yield return new WaitForSeconds(4f, false);
TriggerTutorialSegment(6); // Docking
do
{
//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(6);
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Objective6");
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);
SetHighlight(captain_sonar.Item, false);
SetHighlight(captain_statusMonitor, false);
captain.RemoveActiveObjectiveEntity(captain_navConsole.Item);
GameAnalyticsManager.AddDesignEvent("Tutorial:CaptainTutorial:Completed");
CoroutineManager.StartCoroutine(TutorialCompleted());
}
private void HighlightOrderOption(string option)
{
if (GameMain.GameSession.CrewManager.OrderOptionButtons.Count == 0) return;
var order = GameMain.GameSession.CrewManager.OrderOptionButtons[0].UserData as Order;
int orderIndex = 0;
for (int i = 0; i < GameMain.GameSession.CrewManager.OrderOptionButtons.Count; i++)
{
if (orderIndex >= order.Options.Length)
{
orderIndex = 0;
}
if (order.Options[orderIndex] == option)
{
if (GameMain.GameSession.CrewManager.OrderOptionButtons[i].FlashTimer <= 0)
{
GameMain.GameSession.CrewManager.OrderOptionButtons[i].Flash(highlightColor);
}
}
orderIndex++;
}
}
private bool IsSelectedItem(Item item)
{
return
captain?.SelectedConstruction == item ||
(captain?.SelectedConstruction?.linkedTo?.Contains(item) ?? false);
}
}
}

View File

@@ -1,521 +0,0 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Tutorials
{
class DoctorTutorial : ScenarioTutorial
{
// Room 1
private float shakeTimer = 1f;
private float shakeAmount = 20f;
private LocalizedString radioSpeakerName;
private Character doctor;
private ItemContainer doctor_suppliesCabinet;
private ItemContainer doctor_medBayCabinet;
private Character patient1, patient2;
private List<Character> subPatients;
private Hull medBay;
private Door doctor_firstDoor;
private Door doctor_secondDoor;
private Door doctor_thirdDoor;
private Door tutorial_upperFinalDoor;
private Door tutorial_lockedDoor_2;
private LightComponent doctor_firstDoorLight;
private LightComponent doctor_secondDoorLight;
private LightComponent doctor_thirdDoorLight;
private Door tutorial_submarineDoor;
private LightComponent tutorial_submarineDoorLight;
// Variables
private Sprite doctor_firstAidIcon;
private Color doctor_firstAidIconColor;
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;
subPatients = new List<Character>();
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
doctor = Character.Controlled;
foreach (Item item in doctor.Inventory.AllItemsMod)
{
if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(doctor);
doctor.Inventory.RemoveItem(item);
}
doctor_suppliesCabinet = Item.ItemList.Find(i => i.HasTag("doctor_suppliescabinet"))?.GetComponent<ItemContainer>();
doctor_medBayCabinet = Item.ItemList.Find(i => i.HasTag("doctor_medbaycabinet"))?.GetComponent<ItemContainer>();
var patientHull1 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "waitingroom").CurrentHull;
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, jobOrJobPrefab: JobPrefab.Get("assistant"))
{
TeamID = CharacterTeamType.Team1
};
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
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, jobOrJobPrefab: JobPrefab.Get("assistant"))
{
TeamID = CharacterTeamType.Team1
};
patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2");
patient2.GiveJobItems(null);
patient2.CanSpeak = false;
patient2.AIController.Enabled = false;
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.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, 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, jobOrJobPrefab: JobPrefab.Get("engineer"))
{
TeamID = CharacterTeamType.Team1
};
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
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);
doctor_firstDoor = Item.ItemList.Find(i => i.HasTag("doctor_firstdoor")).GetComponent<Door>();
doctor_secondDoor = Item.ItemList.Find(i => i.HasTag("doctor_seconddoor")).GetComponent<Door>();
doctor_thirdDoor = Item.ItemList.Find(i => i.HasTag("doctor_thirddoor")).GetComponent<Door>();
tutorial_upperFinalDoor = Item.ItemList.Find(i => i.HasTag("tutorial_upperfinaldoor")).GetComponent<Door>();
doctor_firstDoorLight = Item.ItemList.Find(i => i.HasTag("doctor_firstdoorlight")).GetComponent<LightComponent>();
doctor_secondDoorLight = Item.ItemList.Find(i => i.HasTag("doctor_seconddoorlight")).GetComponent<LightComponent>();
doctor_thirdDoorLight = Item.ItemList.Find(i => i.HasTag("doctor_thirddoorlight")).GetComponent<LightComponent>();
SetDoorAccess(doctor_firstDoor, doctor_firstDoorLight, false);
SetDoorAccess(doctor_secondDoor, doctor_secondDoorLight, false);
SetDoorAccess(doctor_thirdDoor, doctor_thirdDoorLight, false);
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);
tutorial_lockedDoor_2 = Item.ItemList.Find(i => i.HasTag("tutorial_lockeddoor_2")).GetComponent<Door>();
SetDoorAccess(tutorial_lockedDoor_2, null, true);
foreach (var patient in subPatients)
{
patient.CanSpeak = false;
patient.AIController.Enabled = false;
patient.GiveJobItems();
}
Item reactorItem = Item.ItemList.Find(i => i.Submarine == Submarine.MainSub && i.GetComponent<Reactor>() != null);
reactorItem.GetComponent<Reactor>().AutoTemp = true;
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Started");
GameAnalyticsManager.AddDesignEvent("Tutorial:Started");
}
public override IEnumerable<CoroutineStatus> UpdateState()
{
while (GameMain.Instance.LoadingScreenOpen) yield return null;
// explosions and radio messages ------------------------------------------------------
yield return new WaitForSeconds(3.0f, false);
//SoundPlayer.PlayDamageSound("StructureBlunt", 10, Character.Controlled.WorldPosition);
//// Room 1
//while (shakeTimer > 0.0f) // Wake up, shake
//{
// shakeTimer -= 0.1f;
// GameMain.GameScreen.Cam.Shake = shakeAmount;
// yield return new WaitForSeconds(0.1f);
//}
//yield return new WaitForSeconds(2.5f);
//GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.WakeUp"), ChatMessageType.Radio, null);
//yield return new WaitForSeconds(2.5f);
doctor.SetStun(1.5f);
var explosion = new Explosion(range: 100, force: 10, damage: 0, structureDamage: 0, itemDamage: 0);
explosion.DisableParticles();
GameMain.GameScreen.Cam.Shake = shakeAmount;
explosion.Explode(Character.Controlled.WorldPosition - Vector2.UnitX * 25, null);
SoundPlayer.PlayDamageSound("StructureBlunt", 10, Character.Controlled.WorldPosition - Vector2.UnitX * 25);
yield return new WaitForSeconds(0.5f, false);
doctor.DamageLimb(
Character.Controlled.WorldPosition,
doctor.AnimController.GetLimb(LimbType.Torso),
new List<Affliction> { new Affliction(AfflictionPrefab.InternalDamage, 10.0f) },
stun: 3.0f, playSound: true, attackImpulse: 0.0f);
shakeTimer = 0.5f;
while (shakeTimer > 0.0f) // Wake up, shake
{
shakeTimer -= 0.1f;
GameMain.GameScreen.Cam.Shake = shakeAmount;
yield return new WaitForSeconds(0.1f, false);
}
yield return new WaitForSeconds(3.0f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Doctor.Radio.KnockedDown"), ChatMessageType.Radio, null);
// first tutorial segment, get medical supplies ------------------------------------------------------
yield return new WaitForSeconds(1.5f, false);
SetHighlight(doctor_suppliesCabinet.Item, true);
/*while (doctor.CurrentHull != doctor_suppliesCabinet.Item.CurrentHull)
{
yield return new WaitForSeconds(2.0f);
}*/
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), "None"); // Medical supplies objective
do
{
for (int i = 0; i < doctor_suppliesCabinet.Inventory.Capacity; i++)
{
if (doctor_suppliesCabinet.Inventory.GetItemAt(i) != null)
{
HighlightInventorySlot(doctor_suppliesCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
}
}
if (doctor.SelectedConstruction == doctor_suppliesCabinet.Item)
{
for (int i = 0; i < doctor.Inventory.Capacity; i++)
{
if (doctor.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(doctor.Inventory, i, highlightColor, .5f, .5f, 0f); }
}
}
yield return null;
} while (doctor.Inventory.FindItemByIdentifier("antidama1".ToIdentifier()) == null); // Wait until looted
yield return new WaitForSeconds(1.0f, false);
SetHighlight(doctor_suppliesCabinet.Item, false);
RemoveCompletedObjective(0);
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Objective0");
yield return new WaitForSeconds(1.0f, false);
// 2nd tutorial segment, treat self -------------------------------------------------------------------------
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(1);
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Objective1");
yield return new WaitForSeconds(1.0f, false);
TriggerTutorialSegment(2); //Treat self
while (doctor.CharacterHealth.GetAfflictionStrength("damage") > 0.01f)
{
if (CharacterHealth.OpenHealthWindow == null)
{
doctor.CharacterHealth.HealthBarPulsateTimer = 1.0f;
}
else
{
HighlightInventorySlot(doctor.Inventory, "antidama1".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return null;
}
RemoveCompletedObjective(2);
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Objective2");
SetDoorAccess(doctor_firstDoor, doctor_firstDoorLight, true);
while (CharacterHealth.OpenHealthWindow != null)
{
yield return new WaitForSeconds(1.0f, false);
}
// treat patient --------------------------------------------------------------------------------------------
//patient 1 requests first aid
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?.Value, givingOrderToSelf: false), ChatMessageType.Order, null);
while (doctor.CurrentHull != patient1.CurrentHull)
{
yield return new WaitForSeconds(1.0f, false);
}
yield return new WaitForSeconds(0.0f, false);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Doctor.Radio.AssistantBurns"), ChatMessageType.Radio, null);
GameMain.GameSession.CrewManager.AllowCharacterSwitch = false;
GameMain.GameSession.CrewManager.AddCharacter(doctor);
GameMain.GameSession.CrewManager.AddCharacter(patient1);
GameMain.GameSession.CrewManager.AutoShowCrewList();
patient1.CharacterHealth.UseHealthWindow = false;
yield return new WaitForSeconds(3.0f, false);
patient1.AIController.Enabled = true;
doctor.RemoveActiveObjectiveEntity(patient1);
TriggerTutorialSegment(3, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Command)); // Get the patient to medbay
while (patient1.GetCurrentOrderWithTopPriority()?.Identifier != "follow")
{
// TODO: Rework order highlighting for new command UI
// GameMain.GameSession.CrewManager.HighlightOrderButton(patient1, "follow", highlightColor, new Vector2(5, 5));
yield return null;
}
SetDoorAccess(doctor_secondDoor, doctor_secondDoorLight, true);
while (patient1.CurrentHull != medBay)
{
yield return new WaitForSeconds(1.0f, false);
}
RemoveCompletedObjective(3);
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Objective3");
SetHighlight(doctor_medBayCabinet.Item, true);
SetDoorAccess(doctor_thirdDoor, doctor_thirdDoorLight, true);
patient1.CharacterHealth.UseHealthWindow = true;
yield return new WaitForSeconds(2.0f, false);
TriggerTutorialSegment(4, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)); // treat burns
do
{
for (int i = 0; i < 3; i++)
{
if (doctor_medBayCabinet.Inventory.GetItemAt(i) != null)
{
HighlightInventorySlot(doctor_medBayCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
}
}
if (doctor.SelectedConstruction == doctor_medBayCabinet.Item)
{
for (int i = 0; i < doctor.Inventory.Capacity; i++)
{
if (doctor.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(doctor.Inventory, i, highlightColor, .5f, .5f, 0f); }
}
}
yield return null;
} while (doctor.Inventory.FindItemByIdentifier("antibleeding1".ToIdentifier()) == null); // Wait until looted
SetHighlight(doctor_medBayCabinet.Item, false);
SetHighlight(patient1, true);
while (patient1.CharacterHealth.GetAfflictionStrength("burn") > 0.01f)
{
if (CharacterHealth.OpenHealthWindow == null)
{
doctor.CharacterHealth.HealthBarPulsateTimer = 1.0f;
}
else
{
HighlightInventorySlot(doctor.Inventory, "antibleeding1".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return null;
}
RemoveCompletedObjective(4);
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Objective4");
SetHighlight(patient1, false);
yield return new WaitForSeconds(1.0f, false);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Doctor.Radio.AssistantBurnsHealed"), ChatMessageType.Radio, null);
// treat unconscious patient ------------------------------------------------------
//patient calls for help
//patient2.CanSpeak = true;
yield return new WaitForSeconds(2.0f, false);
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?.Value, givingOrderToSelf: false), ChatMessageType.Order, null);
patient2.AIController.Enabled = true;
patient2.Oxygen = -50;
CoroutineManager.StartCoroutine(KeepPatientAlive(patient2), "KeepPatient2Alive");
/*while (doctor.CurrentHull != patient2.CurrentHull)
{
yield return new WaitForSeconds(1.0f);
}*/
do { yield return null; } while (!tutorial_upperFinalDoor.IsOpen);
yield return new WaitForSeconds(2.0f, false);
TriggerTutorialSegment(5, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)); // perform CPR
SetHighlight(patient2, true);
while (patient2.IsUnconscious)
{
if (CharacterHealth.OpenHealthWindow != null && doctor.AnimController.Anim != AnimController.Animation.CPR)
{
//Disabled pulse until it's replaced by a better effect
//CharacterHealth.OpenHealthWindow.CPRButton.Pulsate(Vector2.One, Vector2.One * 1.5f, 1.0f);
if (CharacterHealth.OpenHealthWindow.CPRButton.FlashTimer <= 0.0f)
{
CharacterHealth.OpenHealthWindow.CPRButton.Flash(highlightColor);
}
}
yield return null;
}
RemoveCompletedObjective(5);
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Objective5");
SetHighlight(patient2, false);
doctor.RemoveActiveObjectiveEntity(patient2);
CoroutineManager.StopCoroutines("KeepPatient2Alive");
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
while (doctor.Submarine != Submarine.MainSub)
{
yield return new WaitForSeconds(1.0f, false);
}
subPatients[2].Oxygen = -50;
CoroutineManager.StartCoroutine(KeepPatientAlive(subPatients[2]), "KeepPatient3Alive");
yield return new WaitForSeconds(5.0f, false);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Doctor.Radio.EnteredSub"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(3.0f, false);
TriggerTutorialSegment(6, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)); // give treatment to anyone in need
foreach (var patient in subPatients)
{
//patient.CanSpeak = true;
patient.AIController.Enabled = true;
SetHighlight(patient, true);
}
double subEnterTime = Timing.TotalTime;
bool[] patientCalledHelp = new bool[] { false, false, false };
while (subPatients.Any(p => p.Vitality < p.MaxVitality * 0.9f && !p.IsDead))
{
for (int i = 0; i < subPatients.Count; i++)
{
//make patients call for help to make sure the player finds them
//(within 1 minute intervals of entering the sub)
if (!patientCalledHelp[i] && Timing.TotalTime > subEnterTime + 60 * (i + 1))
{
doctor.AddActiveObjectiveEntity(subPatients[i], doctor_firstAidIcon, doctor_firstAidIconColor);
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;
}
if (subPatients[i].ExternalHighlight && subPatients[i].Vitality >= subPatients[i].MaxVitality * 0.9f)
{
doctor.RemoveActiveObjectiveEntity(subPatients[i]);
SetHighlight(subPatients[i], false);
}
}
yield return new WaitForSeconds(1.0f, false);
}
RemoveCompletedObjective(6);
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Objective6");
foreach (var patient in subPatients)
{
SetHighlight(patient, false);
doctor.RemoveActiveObjectiveEntity(patient);
}
// END TUTORIAL
GameAnalyticsManager.AddDesignEvent("Tutorial:DoctorTutorial:Completed");
CoroutineManager.StartCoroutine(TutorialCompleted());
}
public IEnumerable<CoroutineStatus> KeepPatientAlive(Character patient)
{
while (patient != null && !patient.Removed)
{
patient.Oxygen = Math.Max(patient.Oxygen, -50);
yield return null;
}
}
}
}

View File

@@ -1,664 +0,0 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma.Tutorials
{
class EngineerTutorial : ScenarioTutorial
{
// Other tutorial items
private LightComponent tutorial_securityFinalDoorLight;
private LightComponent tutorial_mechanicFinalDoorLight;
private Steering tutorial_submarineSteering;
// Room 1
private float shakeTimer = 1f;
private float shakeAmount = 20f;
// Room 2
private MotionSensor engineer_equipmentObjectiveSensor;
private ItemContainer engineer_equipmentCabinet;
private Door engineer_firstDoor;
private LightComponent engineer_firstDoorLight;
// Room 3
private Powered tutorial_oxygenGenerator;
private Reactor engineer_reactor;
private Door engineer_secondDoor;
private LightComponent engineer_secondDoorLight;
// Room 4
private Item engineer_brokenJunctionBox;
private Door engineer_thirdDoor;
private LightComponent engineer_thirdDoorLight;
// Room 5
private PowerTransfer[] engineer_disconnectedJunctionBoxes;
private ConnectionPanel[] engineer_disconnectedConnectionPanels;
private Item engineer_wire_1;
private Powered engineer_lamp_1;
private Item engineer_wire_2;
private Powered engineer_lamp_2;
private Door engineer_fourthDoor;
private LightComponent engineer_fourthDoorLight;
// Room 6
private Pump engineer_workingPump;
private Door tutorial_lockedDoor_1;
// Submarine
private Door tutorial_submarineDoor;
private LightComponent tutorial_submarineDoorLight;
private MotionSensor tutorial_enteredSubmarineSensor;
private Item engineer_submarineJunctionBox_1;
private Item engineer_submarineJunctionBox_2;
private Item engineer_submarineJunctionBox_3;
private Reactor engineer_submarineReactor;
// Variables
private LocalizedString radioSpeakerName;
private Character engineer;
private int[] reactorLoads = new int[5] { 1500, 3000, 2000, 5000, 3500 };
private float reactorLoadChangeTime = 2f;
private float reactorLoadError = 200f;
private bool reactorOperatedProperly;
private const float waterVolumeBeforeOpening = 15f;
private Sprite engineer_repairIcon;
private Color engineer_repairIconColor;
private Sprite engineer_reactorIcon;
private Color engineer_reactorIconColor;
private bool wiringActive = false;
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)));
}
protected override void Initialize()
{
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
engineer = Character.Controlled;
foreach (Item item in engineer.Inventory.AllItemsMod)
{
if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(engineer);
engineer.Inventory.RemoveItem(item);
}
var repairOrder = OrderPrefab.Prefabs["repairsystems"];
engineer_repairIcon = repairOrder.SymbolSprite;
engineer_repairIconColor = repairOrder.Color;
var reactorOrder = OrderPrefab.Prefabs["operatereactor"];
engineer_reactorIcon = reactorOrder.SymbolSprite;
engineer_reactorIconColor = reactorOrder.Color;
// Other tutorial items
tutorial_securityFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_securityfinaldoorlight")).GetComponent<LightComponent>();
tutorial_mechanicFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoorlight")).GetComponent<LightComponent>();
tutorial_submarineSteering = Item.ItemList.Find(i => i.HasTag("command")).GetComponent<Steering>();
tutorial_submarineSteering.CanBeSelected = false;
foreach (ItemComponent ic in tutorial_submarineSteering.Item.Components)
{
ic.CanBeSelected = false;
}
SetDoorAccess(null, tutorial_securityFinalDoorLight, false);
SetDoorAccess(null, tutorial_mechanicFinalDoorLight, false);
// Room 2
engineer_equipmentObjectiveSensor = Item.ItemList.Find(i => i.HasTag("engineer_equipmentobjectivesensor")).GetComponent<MotionSensor>();
engineer_equipmentCabinet = Item.ItemList.Find(i => i.HasTag("engineer_equipmentcabinet")).GetComponent<ItemContainer>();
engineer_firstDoor = Item.ItemList.Find(i => i.HasTag("engineer_firstdoor")).GetComponent<Door>();
engineer_firstDoorLight = Item.ItemList.Find(i => i.HasTag("engineer_firstdoorlight")).GetComponent<LightComponent>();
SetDoorAccess(engineer_firstDoor, engineer_firstDoorLight, false);
// Room 3
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;
engineer_reactor.FuelConsumptionRate = 0.0f;
engineer_reactor.PowerOn = true;
reactorOperatedProperly = false;
engineer_secondDoor = Item.ItemList.Find(i => i.HasTag("engineer_seconddoor")).GetComponent<Door>(); ;
engineer_secondDoorLight = Item.ItemList.Find(i => i.HasTag("engineer_seconddoorlight")).GetComponent<LightComponent>();
SetDoorAccess(engineer_secondDoor, engineer_secondDoorLight, false);
// Room 4
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>();
engineer_brokenJunctionBox.Indestructible = false;
engineer_brokenJunctionBox.Condition = 0f;
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, false);
// Room 5
engineer_disconnectedJunctionBoxes = new PowerTransfer[4];
engineer_disconnectedConnectionPanels = new ConnectionPanel[4];
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
{
engineer_disconnectedJunctionBoxes[i] = Item.ItemList.Find(item => item.HasTag($"engineer_disconnectedjunctionbox_{i + 1}")).GetComponent<PowerTransfer>();
engineer_disconnectedConnectionPanels[i] = engineer_disconnectedJunctionBoxes[i].Item.GetComponent<ConnectionPanel>();
engineer_disconnectedConnectionPanels[i].Locked = false;
for (int j = 0; j < engineer_disconnectedJunctionBoxes[i].PowerConnections.Count; j++)
{
foreach (Wire wire in engineer_disconnectedJunctionBoxes[i].PowerConnections[j].Wires)
{
if (wire == null) continue;
wire.Locked = true;
}
}
}
engineer_wire_1 = Item.ItemList.Find(i => i.HasTag("engineer_wire_1"));
engineer_wire_1.SpriteColor = Color.Transparent;
engineer_wire_2 = Item.ItemList.Find(i => i.HasTag("engineer_wire_2"));
engineer_wire_2.SpriteColor = Color.Transparent;
engineer_lamp_1 = Item.ItemList.Find(i => i.HasTag("engineer_lamp_1")).GetComponent<Powered>();
engineer_lamp_2 = Item.ItemList.Find(i => i.HasTag("engineer_lamp_2")).GetComponent<Powered>();
engineer_fourthDoor = Item.ItemList.Find(i => i.HasTag("engineer_fourthdoor")).GetComponent<Door>();
engineer_fourthDoorLight = Item.ItemList.Find(i => i.HasTag("engineer_fourthdoorlight")).GetComponent<LightComponent>();
SetDoorAccess(engineer_fourthDoor, engineer_fourthDoorLight, false);
// Room 6
engineer_workingPump = Item.ItemList.Find(i => i.HasTag("engineer_workingpump")).GetComponent<Pump>();
engineer_workingPump.Item.CurrentHull.WaterVolume += engineer_workingPump.Item.CurrentHull.Volume;
engineer_workingPump.IsActive = true;
tutorial_lockedDoor_1 = Item.ItemList.Find(i => i.HasTag("tutorial_lockeddoor_1")).GetComponent<Door>();
SetDoorAccess(tutorial_lockedDoor_1, null, true);
// Submarine
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
tutorial_enteredSubmarineSensor = Item.ItemList.Find(i => i.HasTag("tutorial_enteredsubmarinesensor")).GetComponent<MotionSensor>();
engineer_submarineJunctionBox_1 = Item.ItemList.Find(i => i.HasTag("engineer_submarinejunctionbox_1"));
engineer_submarineJunctionBox_2 = Item.ItemList.Find(i => i.HasTag("engineer_submarinejunctionbox_2"));
engineer_submarineJunctionBox_3 = Item.ItemList.Find(i => i.HasTag("engineer_submarinejunctionbox_3"));
engineer_submarineReactor = Item.ItemList.Find(i => i.HasTag("engineer_submarinereactor")).GetComponent<Reactor>();
engineer_submarineReactor.PowerOn = true;
engineer_submarineReactor.IsActive = engineer_submarineReactor.AutoTemp = false;
engineer_submarineJunctionBox_1.Indestructible = false;
engineer_submarineJunctionBox_1.Condition = 0f;
engineer_submarineJunctionBox_2.Indestructible = false;
engineer_submarineJunctionBox_2.Condition = 0f;
engineer_submarineJunctionBox_3.Indestructible = false;
engineer_submarineJunctionBox_3.Condition = 0f;
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Started");
GameAnalyticsManager.AddDesignEvent("Tutorial:Started");
}
public override IEnumerable<CoroutineStatus> UpdateState()
{
while (GameMain.Instance.LoadingScreenOpen) { yield return null; }
// Room 1
SoundPlayer.PlayDamageSound("StructureBlunt", 10, Character.Controlled.WorldPosition);
while (shakeTimer > 0.0f) // Wake up, shake
{
shakeTimer -= 0.1f;
GameMain.GameScreen.Cam.Shake = shakeAmount;
yield return new WaitForSeconds(0.1f, false);
}
//// Remove
//for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
//{
// SetHighlight(engineer_disconnectedJunctionBoxes[i].Item, true);
//}
//do { CheckGhostWires(); HandleJunctionBoxWiringHighlights(); yield return null; } while (engineer_workingPump.Voltage < engineer_workingPump.MinVoltage); // Wait until connected all the way to the pump
//CheckGhostWires();
//// Remove
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.WakeUp"), ChatMessageType.Radio, null);
SetHighlight(engineer_equipmentCabinet.Item, true);
// Room 2
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, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Retrieve equipment
bool firstSlotRemoved = false;
bool secondSlotRemoved = false;
bool thirdSlotRemoved = false;
bool fourthSlotRemoved = false;
do
{
if (IsSelectedItem(engineer_equipmentCabinet.Item))
{
if (!firstSlotRemoved)
{
HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f);
if (engineer_equipmentCabinet.Inventory.GetItemAt(0) == null) { firstSlotRemoved = true; }
}
if (!secondSlotRemoved)
{
HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f);
if (engineer_equipmentCabinet.Inventory.GetItemAt(1) == null) { secondSlotRemoved = true; }
}
if (!thirdSlotRemoved)
{
HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f);
if (engineer_equipmentCabinet.Inventory.GetItemAt(2) == null) { thirdSlotRemoved = true; }
}
if (!fourthSlotRemoved)
{
HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 3, highlightColor, .5f, .5f, 0f);
if (engineer_equipmentCabinet.Inventory.GetItemAt(2) == null) { fourthSlotRemoved = true; }
}
for (int i = 0; i < engineer.Inventory.visualSlots.Length; i++)
{
if (engineer.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(engineer.Inventory, i, highlightColor, .5f, .5f, 0f); }
}
}
yield return null;
} while (!engineer_equipmentCabinet.Inventory.IsEmpty()); // Wait until looted
RemoveCompletedObjective(0);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective0");
SetHighlight(engineer_equipmentCabinet.Item, false);
SetHighlight(engineer_reactor.Item, true);
SetDoorAccess(engineer_firstDoor, engineer_firstDoorLight, true);
// Room 3
do { yield return null; } while (!IsSelectedItem(engineer_reactor.Item));
yield return new WaitForSeconds(0.5f, false);
TriggerTutorialSegment(1);
do
{
if (IsSelectedItem(engineer_reactor.Item))
{
engineer_reactor.AutoTemp = false;
if (engineer_reactor.PowerButton.FlashTimer <= 0)
{
engineer_reactor.PowerButton.Flash(highlightColor, 1.5f, false);
}
}
yield return null;
} while (!engineer_reactor.PowerOn);
do
{
if (IsSelectedItem(engineer_reactor.Item) && engineer_reactor.Item.OwnInventory.visualSlots != null)
{
engineer_reactor.AutoTemp = false;
HighlightInventorySlot(engineer.Inventory, "fuelrod".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
for (int i = 0; i < engineer_reactor.Item.OwnInventory.visualSlots.Length; i++)
{
HighlightInventorySlot(engineer_reactor.Item.OwnInventory, i, highlightColor, 0.5f, 0.5f, 0f);
}
}
yield return null;
} while (engineer_reactor.AvailableFuel == 0);
RemoveCompletedObjective(1);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective1");
TriggerTutorialSegment(2);
CoroutineManager.StartCoroutine(ReactorOperatedProperly());
do
{
if (IsSelectedItem(engineer_reactor.Item))
{
engineer_reactor.AutoTemp = false;
if (engineer_reactor.FissionRateScrollBar.FlashTimer <= 0)
{
engineer_reactor.FissionRateScrollBar.Flash(highlightColor, 1.5f);
}
if (engineer_reactor.TurbineOutputScrollBar.FlashTimer <= 0)
{
engineer_reactor.TurbineOutputScrollBar.Flash(highlightColor, 1.5f);
}
}
yield return null;
} while (!reactorOperatedProperly);
yield return new WaitForSeconds(2f, false);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.ReactorStable"), ChatMessageType.Radio, null);
do
{
if (IsSelectedItem(engineer_reactor.Item))
{
if (engineer_reactor.AutoTempSwitch.FlashTimer <= 0)
{
engineer_reactor.AutoTempSwitch.Flash(highlightColor, 1.5f, false, false, new Vector2(10, 10));
}
}
yield return null;
} while (!engineer_reactor.AutoTemp);
float wait = 1.5f;
do
{
yield return new WaitForSeconds(0.1f, false);
wait -= 0.1f;
engineer_reactor.AutoTemp = true;
} while (wait > 0.0f);
engineer.SelectedConstruction = null;
engineer_reactor.CanBeSelected = false;
RemoveCompletedObjective(2);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective2");
SetHighlight(engineer_reactor.Item, false);
SetHighlight(engineer_brokenJunctionBox, true);
SetDoorAccess(engineer_secondDoor, engineer_secondDoorLight, true);
// Room 4
do { yield return null; } while (!engineer_secondDoor.IsOpen);
yield return new WaitForSeconds(1f, false);
Repairable repairableJunctionBoxComponent = engineer_brokenJunctionBox.GetComponent<Repairable>();
TriggerTutorialSegment(3, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select)); // Repair the junction box
do
{
if (!engineer.HasEquippedItem("screwdriver".ToIdentifier()))
{
HighlightInventorySlot(engineer.Inventory, "screwdriver".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
else if (IsSelectedItem(engineer_brokenJunctionBox) && repairableJunctionBoxComponent.CurrentFixer == null)
{
if (repairableJunctionBoxComponent.RepairButton.FlashTimer <= 0)
{
repairableJunctionBoxComponent.RepairButton.Flash();
}
}
yield return null;
} while (repairableJunctionBoxComponent.IsBelowRepairThreshold); // Wait until repaired
SetHighlight(engineer_brokenJunctionBox, false);
RemoveCompletedObjective(3);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective3");
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, true);
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
{
SetHighlight(engineer_disconnectedJunctionBoxes[i].Item, true);
}
// Room 5
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, 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(4);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective4");
do { yield return null; } while (engineer_workingPump.Item.CurrentHull.WaterPercentage > waterVolumeBeforeOpening); // Wait until drained
wiringActive = false;
SetDoorAccess(engineer_fourthDoor, engineer_fourthDoorLight, true);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.ChangeOfPlans"), ChatMessageType.Radio, null);
// Submarine
do { yield return null; } while (!tutorial_enteredSubmarineSensor.MotionDetected);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Submarine"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(5); // Repair junction box
while (ContentRunning) yield return null;
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_1, engineer_repairIcon, engineer_repairIconColor);
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_2, engineer_repairIcon, engineer_repairIconColor);
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_3, engineer_repairIcon, engineer_repairIconColor);
SetHighlight(engineer_submarineJunctionBox_1, true);
SetHighlight(engineer_submarineJunctionBox_2, true);
SetHighlight(engineer_submarineJunctionBox_3, true);
Repairable repairableJunctionBoxComponent1 = engineer_submarineJunctionBox_1.GetComponent<Repairable>();
Repairable repairableJunctionBoxComponent2 = engineer_submarineJunctionBox_2.GetComponent<Repairable>();
Repairable repairableJunctionBoxComponent3 = engineer_submarineJunctionBox_3.GetComponent<Repairable>();
// 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(5);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective5");
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(6); // Powerup reactor
SetHighlight(engineer_submarineReactor.Item, true);
engineer.AddActiveObjectiveEntity(engineer_submarineReactor.Item, engineer_reactorIcon, engineer_reactorIconColor);
do { yield return null; } while (!IsReactorPoweredUp(engineer_submarineReactor)); // Wait until ~matches load
engineer.RemoveActiveObjectiveEntity(engineer_submarineReactor.Item);
SetHighlight(engineer_submarineReactor.Item, false);
RemoveCompletedObjective(6);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective6");
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Complete"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(4f, false);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Completed");
CoroutineManager.StartCoroutine(TutorialCompleted());
}
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (wiringActive)
{
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
{
for (int j = 0; j < engineer_disconnectedJunctionBoxes[i].PowerConnections.Count; j++)
{
engineer_disconnectedJunctionBoxes[i].PowerConnections[j].UpdateFlashTimer(deltaTime);
}
}
}
}
private bool IsSelectedItem(Item item)
{
return engineer?.SelectedConstruction == item;
}
private IEnumerable<CoroutineStatus> ReactorOperatedProperly()
{
float timer;
for (int i = 0; i < reactorLoads.Length; i++)
{
timer = reactorLoadChangeTime;
tutorial_oxygenGenerator.PowerConsumption = reactorLoads[i];
while (timer > 0)
{
yield return CoroutineStatus.Running;
if (CoroutineManager.DeltaTime > 0.0f && IsReactorPoweredUp(engineer_reactor))
{
timer -= CoroutineManager.DeltaTime;
}
}
}
reactorOperatedProperly = true;
}
private void CheckGhostWires()
{
Color wireColor =
Color.Orange *
MathHelper.Lerp(0.25f, 0.75f, (float)(Math.Sin((Timing.TotalTime * 4.0f)) + 1.0f) / 2.0f);
if (engineer_wire_1 != null)
{
engineer_wire_1.SpriteColor = wireColor;
if (engineer_lamp_1.Voltage > engineer_lamp_1.MinVoltage)
{
engineer_wire_1.Remove();
engineer_wire_1 = null;
}
}
if (engineer_wire_2 != null)
{
engineer_wire_2.SpriteColor = wireColor;
if (engineer_lamp_2.Voltage > engineer_lamp_2.MinVoltage)
{
engineer_wire_2.Remove();
engineer_wire_2 = null;
}
}
}
private void HandleJunctionBoxWiringHighlights()
{
Item selected = engineer.SelectedConstruction;
if (!engineer.HasEquippedItem("screwdriver".ToIdentifier()))
{
HighlightInventorySlot(engineer.Inventory, "screwdriver".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
}
int selectedIndex = -1;
if (selected != null)
{
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
{
if (selected == engineer_disconnectedJunctionBoxes[i].Item)
{
selectedIndex = i;
break;
}
}
}
wiringActive = selectedIndex != -1;
if (!engineer.HasEquippedItem("wire".ToIdentifier()))
{
HighlightInventorySlotWithTag(engineer.Inventory, "wire".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
}
else
{
if (!wiringActive) return;
for (int i = 0; i < engineer_disconnectedConnectionPanels[selectedIndex].Connections.Count; i++)
{
var connection = engineer_disconnectedConnectionPanels[selectedIndex].Connections[i];
if (connection.IsPower && connection.FlashTimer <= 0)
{
foreach (Wire wire in engineer_disconnectedConnectionPanels[selectedIndex].Connections[i].Wires)
{
if (wire == null) continue;
if (!wire.Locked)
{
return;
}
}
connection.Flash(highlightColor);
}
}
}
}
private void CheckJunctionBoxHighlights(Repairable comp1, Repairable comp2, Repairable comp3)
{
if (!comp1.IsBelowRepairThreshold && engineer_submarineJunctionBox_1.ExternalHighlight)
{
SetHighlight(engineer_submarineJunctionBox_1, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_1);
}
if (!comp2.IsBelowRepairThreshold && engineer_submarineJunctionBox_2.ExternalHighlight)
{
SetHighlight(engineer_submarineJunctionBox_2, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_2);
}
if (!comp3.IsBelowRepairThreshold && engineer_submarineJunctionBox_3.ExternalHighlight)
{
SetHighlight(engineer_submarineJunctionBox_3, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_3);
}
}
private bool IsReactorPoweredUp(Reactor reactor)
{
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) < reactorLoadError;
}
}
}

View File

@@ -1,737 +0,0 @@
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma.Tutorials
{
class MechanicTutorial : ScenarioTutorial
{
// Other tutorial items
private LightComponent tutorial_securityFinalDoorLight;
private Door tutorial_upperFinalDoor;
private Steering tutorial_submarineSteering;
// Room 1
private float shakeTimer = 1f;
private float shakeAmount = 20f;
private Door mechanic_firstDoor;
private LightComponent mechanic_firstDoorLight;
// Room 2
private MotionSensor mechanic_equipmentObjectiveSensor;
private ItemContainer mechanic_equipmentCabinet;
private Door mechanic_secondDoor;
private LightComponent mechanic_secondDoorLight;
// Room 3
private MotionSensor mechanic_weldingObjectiveSensor;
private Pump mechanic_workingPump;
private Door mechanic_thirdDoor;
private LightComponent mechanic_thirdDoorLight;
private Structure mechanic_brokenWall_1;
private Hull mechanic_brokenhull_1;
// Room 4
private MotionSensor mechanic_craftingObjectiveSensor;
private Deconstructor mechanic_deconstructor;
private Fabricator mechanic_fabricator;
private ItemContainer mechanic_craftingCabinet;
private Door mechanic_fourthDoor;
private LightComponent mechanic_fourthDoorLight;
// Room 5
private MotionSensor mechanic_fireSensor;
private DummyFireSource mechanic_fire;
private Door mechanic_fifthDoor;
private LightComponent mechanic_fifthDoorLight;
// Room 6
private MotionSensor mechanic_divingSuitObjectiveSensor;
private ItemContainer mechanic_divingSuitContainer;
private ItemContainer mechanic_oxygenContainer;
private Door tutorial_mechanicFinalDoor;
private LightComponent tutorial_mechanicFinalDoorLight;
// Room 7
private Pump mechanic_brokenPump;
private Structure mechanic_brokenWall_2;
private Hull mechanic_brokenhull_2;
private Door tutorial_submarineDoor;
private LightComponent tutorial_submarineDoorLight;
// Submarine
private MotionSensor tutorial_enteredSubmarineSensor;
private Engine mechanic_submarineEngine;
private Pump mechanic_ballastPump_1;
private Pump mechanic_ballastPump_2;
// Variables
private const float waterVolumeBeforeOpening = 15f;
private LocalizedString radioSpeakerName;
private Character mechanic;
private Sprite mechanic_repairIcon;
private Color mechanic_repairIconColor;
private Sprite mechanic_weldIcon;
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)));
}
protected override void Initialize()
{
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
mechanic = Character.Controlled;
foreach (Item item in mechanic.Inventory.AllItemsMod)
{
if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(mechanic);
mechanic.Inventory.RemoveItem(item);
}
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));
// Other tutorial items
tutorial_securityFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_securityfinaldoorlight")).GetComponent<LightComponent>();
tutorial_upperFinalDoor = Item.ItemList.Find(i => i.HasTag("tutorial_upperfinaldoor")).GetComponent<Door>();
tutorial_submarineSteering = Item.ItemList.Find(i => i.HasTag("command")).GetComponent<Steering>();
tutorial_submarineSteering.CanBeSelected = false;
foreach (ItemComponent ic in tutorial_submarineSteering.Item.Components)
{
ic.CanBeSelected = false;
}
SetDoorAccess(null, tutorial_securityFinalDoorLight, false);
SetDoorAccess(tutorial_upperFinalDoor, null, false);
// Room 1
mechanic_firstDoor = Item.ItemList.Find(i => i.HasTag("mechanic_firstdoor")).GetComponent<Door>();
mechanic_firstDoorLight = Item.ItemList.Find(i => i.HasTag("mechanic_firstdoorlight")).GetComponent<LightComponent>();
SetDoorAccess(mechanic_firstDoor, mechanic_firstDoorLight, false);
// Room 2
mechanic_equipmentObjectiveSensor = Item.ItemList.Find(i => i.HasTag("mechanic_equipmentobjectivesensor")).GetComponent<MotionSensor>();
mechanic_equipmentCabinet = Item.ItemList.Find(i => i.HasTag("mechanic_equipmentcabinet")).GetComponent<ItemContainer>();
mechanic_secondDoor = Item.ItemList.Find(i => i.HasTag("mechanic_seconddoor")).GetComponent<Door>();
mechanic_secondDoorLight = Item.ItemList.Find(i => i.HasTag("mechanic_seconddoorlight")).GetComponent<LightComponent>();
SetDoorAccess(mechanic_secondDoor, mechanic_secondDoorLight, false);
// Room 3
mechanic_weldingObjectiveSensor = Item.ItemList.Find(i => i.HasTag("mechanic_weldingobjectivesensor")).GetComponent<MotionSensor>();
mechanic_workingPump = Item.ItemList.Find(i => i.HasTag("mechanic_workingpump")).GetComponent<Pump>();
mechanic_thirdDoor = Item.ItemList.Find(i => i.HasTag("mechanic_thirddoor")).GetComponent<Door>();
mechanic_thirdDoorLight = Item.ItemList.Find(i => i.HasTag("mechanic_thirddoorlight")).GetComponent<LightComponent>();
mechanic_brokenWall_1 = Structure.WallList.Find(i => i.SpecialTag == "mechanic_brokenwall_1");
//mechanic_ladderSensor = Item.ItemList.Find(i => i.HasTag("mechanic_laddersensor")).GetComponent<MotionSensor>();
SetDoorAccess(mechanic_thirdDoor, mechanic_thirdDoorLight, false);
mechanic_brokenWall_1.Indestructible = false;
mechanic_brokenWall_1.SpriteColor = Color.White;
for (int i = 0; i < mechanic_brokenWall_1.SectionCount; i++)
{
mechanic_brokenWall_1.AddDamage(i, 85);
}
mechanic_brokenhull_1 = mechanic_brokenWall_1.Sections[0].gap.FlowTargetHull;
// Room 4
mechanic_craftingObjectiveSensor = Item.ItemList.Find(i => i.HasTag("mechanic_craftingobjectivesensor")).GetComponent<MotionSensor>();
mechanic_deconstructor = Item.ItemList.Find(i => i.HasTag("mechanic_deconstructor")).GetComponent<Deconstructor>();
mechanic_fabricator = Item.ItemList.Find(i => i.HasTag("mechanic_fabricator")).GetComponent<Fabricator>();
mechanic_craftingCabinet = Item.ItemList.Find(i => i.HasTag("mechanic_craftingcabinet")).GetComponent<ItemContainer>();
mechanic_fourthDoor = Item.ItemList.Find(i => i.HasTag("mechanic_fourthdoor")).GetComponent<Door>();
mechanic_fourthDoorLight = Item.ItemList.Find(i => i.HasTag("mechanic_fourthdoorlight")).GetComponent<LightComponent>();
SetDoorAccess(mechanic_fourthDoor, mechanic_fourthDoorLight, false);
// Room 5
mechanic_fifthDoor = Item.ItemList.Find(i => i.HasTag("mechanic_fifthdoor")).GetComponent<Door>();
mechanic_fifthDoorLight = Item.ItemList.Find(i => i.HasTag("mechanic_fifthdoorlight")).GetComponent<LightComponent>();
mechanic_fireSensor = Item.ItemList.Find(i => i.HasTag("mechanic_firesensor")).GetComponent<MotionSensor>();
SetDoorAccess(mechanic_fifthDoor, mechanic_fifthDoorLight, false);
// Room 6
mechanic_divingSuitObjectiveSensor = Item.ItemList.Find(i => i.HasTag("mechanic_divingsuitobjectivesensor")).GetComponent<MotionSensor>();
mechanic_divingSuitContainer = Item.ItemList.Find(i => i.HasTag("mechanic_divingsuitcontainer")).GetComponent<ItemContainer>();
foreach (Item item in mechanic_divingSuitContainer.Inventory.AllItems)
{
foreach (ItemComponent ic in item.Components)
{
ic.CanBePicked = true;
}
}
mechanic_oxygenContainer = Item.ItemList.Find(i => i.HasTag("mechanic_oxygencontainer")).GetComponent<ItemContainer>();
foreach (Item item in mechanic_oxygenContainer.Inventory.AllItems)
{
foreach (ItemComponent ic in item.Components)
{
ic.CanBePicked = true;
}
}
tutorial_mechanicFinalDoor = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoor")).GetComponent<Door>();
tutorial_mechanicFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoorlight")).GetComponent<LightComponent>();
SetDoorAccess(tutorial_mechanicFinalDoor, tutorial_mechanicFinalDoorLight, false);
// Room 7
mechanic_brokenPump = Item.ItemList.Find(i => i.HasTag("mechanic_brokenpump")).GetComponent<Pump>();
mechanic_brokenPump.Item.Indestructible = false;
mechanic_brokenPump.Item.Condition = 0;
mechanic_brokenPump.CanBeSelected = false;
mechanic_brokenPump.Item.GetComponent<Repairable>().CanBeSelected = false;
mechanic_brokenWall_2 = Structure.WallList.Find(i => i.SpecialTag == "mechanic_brokenwall_2");
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
mechanic_brokenWall_2.Indestructible = false;
mechanic_brokenWall_2.SpriteColor = Color.White;
for (int i = 0; i < mechanic_brokenWall_2.SectionCount; i++)
{
mechanic_brokenWall_2.AddDamage(i, 85);
}
mechanic_brokenhull_2 = mechanic_brokenWall_2.Sections[0].gap.FlowTargetHull;
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);
// Submarine
tutorial_enteredSubmarineSensor = Item.ItemList.Find(i => i.HasTag("tutorial_enteredsubmarinesensor")).GetComponent<MotionSensor>();
mechanic_submarineEngine = Item.ItemList.Find(i => i.HasTag("mechanic_submarineengine")).GetComponent<Engine>();
mechanic_submarineEngine.Item.Indestructible = false;
mechanic_submarineEngine.Item.Condition = 0f;
mechanic_ballastPump_1 = Item.ItemList.Find(i => i.HasTag("mechanic_ballastpump_1")).GetComponent<Pump>();
mechanic_ballastPump_1.Item.Indestructible = false;
mechanic_ballastPump_1.Item.Condition = 0f;
mechanic_ballastPump_2 = Item.ItemList.Find(i => i.HasTag("mechanic_ballastpump_2")).GetComponent<Pump>();
mechanic_ballastPump_2.Item.Indestructible = false;
mechanic_ballastPump_2.Item.Condition = 0f;
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Started");
GameAnalyticsManager.AddDesignEvent("Tutorial:Started");
}
public override void Update(float deltaTime)
{
if (mechanic_brokenhull_1 != null)
{
mechanic_brokenhull_1.WaterVolume = MathHelper.Clamp(mechanic_brokenhull_1.WaterVolume, 0, mechanic_brokenhull_1.Volume * 0.85f);
}
base.Update(deltaTime);
}
public override IEnumerable<CoroutineStatus> UpdateState()
{
while (GameMain.Instance.LoadingScreenOpen) yield return null;
// Room 1
SoundPlayer.PlayDamageSound("StructureBlunt", 10, Character.Controlled.WorldPosition);
while (shakeTimer > 0.0f) // Wake up, shake
{
shakeTimer -= 0.1f;
GameMain.GameScreen.Cam.Shake = shakeAmount;
yield return new WaitForSeconds(0.1f, false);
}
yield return new WaitForSeconds(2.5f, false);
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, 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(0);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective0");
// 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, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Equipment & inventory objective
SetHighlight(mechanic_equipmentCabinet.Item, true);
bool firstSlotRemoved = false;
bool secondSlotRemoved = false;
bool thirdSlotRemoved = false;
do
{
if (IsSelectedItem(mechanic_equipmentCabinet.Item))
{
if (!firstSlotRemoved)
{
HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f);
if (mechanic_equipmentCabinet.Inventory.GetItemAt(0) == null) { firstSlotRemoved = true; }
}
if (!secondSlotRemoved)
{
HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f);
if (mechanic_equipmentCabinet.Inventory.GetItemAt(1) == null) { secondSlotRemoved = true; }
}
if (!thirdSlotRemoved)
{
HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f);
if (mechanic_equipmentCabinet.Inventory.GetItemAt(2) == null) { thirdSlotRemoved = true; }
}
for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{
if (mechanic.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); }
}
}
yield return null;
} 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(1);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective1");
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, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot)); // Welding objective
do
{
if (!mechanic.HasEquippedItem("divingmask".ToIdentifier()))
{
HighlightInventorySlot(mechanic.Inventory, "divingmask".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
if (!mechanic.HasEquippedItem("weldingtool".ToIdentifier()))
{
HighlightInventorySlot(mechanic.Inventory, "weldingtool".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return null;
} 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(2);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective2");
yield return new WaitForSeconds(1f, false);
TriggerTutorialSegment(3, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select)); // Pump objective
SetHighlight(mechanic_workingPump.Item, true);
do
{
yield return null;
if (IsSelectedItem(mechanic_workingPump.Item))
{
if (mechanic_workingPump.PowerButton.FlashTimer <= 0)
{
mechanic_workingPump.PowerButton.Flash(uiHighlightColor, 1.5f, true);
}
}
} while (mechanic_workingPump.FlowPercentage >= 0 || !mechanic_workingPump.IsActive); // Highlight until draining
SetHighlight(mechanic_workingPump.Item, false);
do { yield return null; } while (mechanic_brokenhull_1 != null && mechanic_brokenhull_1.WaterPercentage > waterVolumeBeforeOpening); // Unlock door once drained
RemoveCompletedObjective(3);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective3");
SetDoorAccess(mechanic_thirdDoor, mechanic_thirdDoorLight, true);
//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);
yield return new WaitForSeconds(1f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Fire"), ChatMessageType.Radio, null);
// Room 4
do { yield return null; } while (!mechanic_thirdDoor.IsOpen);
yield return new WaitForSeconds(1f, false);
mechanic_fire = new DummyFireSource(new Vector2(20f, 2f), Item.ItemList.Find(i => i.HasTag("mechanic_fire")).WorldPosition);
//do { yield return null; } while (!mechanic_craftingObjectiveSensor.MotionDetected);
TriggerTutorialSegment(4); // Deconstruct
SetHighlight(mechanic_craftingCabinet.Item, true);
bool gotOxygenTank = false;
bool gotSodium = false;
do
{
if (mechanic.SelectedConstruction == mechanic_craftingCabinet.Item)
{
for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{
if (mechanic.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); }
}
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")
{
HighlightInventorySlot(mechanic_craftingCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
}
}
}
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")
{
HighlightInventorySlot(mechanic_craftingCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
}
}
}
}
if (!gotOxygenTank && (mechanic.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) != null ||
mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) != null))
{
gotOxygenTank = true;
}
if (!gotSodium && mechanic.Inventory.FindItemByIdentifier("sodium".ToIdentifier()) != null)
{
gotSodium = true;
}
yield return null;
} while (!gotOxygenTank || !gotSodium); // Wait until looted
yield return new WaitForSeconds(1.0f, false);
SetHighlight(mechanic_craftingCabinet.Item, false);
SetHighlight(mechanic_deconstructor.Item, true);
do
{
if (IsSelectedItem(mechanic_deconstructor.Item))
{
if (mechanic_deconstructor.OutputContainer.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) != null)
{
HighlightInventorySlot(mechanic_deconstructor.OutputContainer.Inventory, "aluminium".ToIdentifier(), highlightColor, .5f, .5f, 0f);
for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{
if (mechanic.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); }
}
}
else
{
if (mechanic.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) != null && mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank".ToIdentifier()) == null)
{
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".ToIdentifier()) != null && !mechanic_deconstructor.IsActive)
{
if (mechanic_deconstructor.ActivateButton.FlashTimer <= 0)
{
mechanic_deconstructor.ActivateButton.Flash(highlightColor, 1.5f, false);
}
}
}
}
yield return null;
} while (
mechanic.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) == null &&
mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("aluminium".ToIdentifier()) == null); // Wait until aluminium obtained
SetHighlight(mechanic_deconstructor.Item, false);
RemoveCompletedObjective(4);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective4");
yield return new WaitForSeconds(1f, false);
TriggerTutorialSegment(5); // Fabricate
SetHighlight(mechanic_fabricator.Item, true);
do
{
if (IsSelectedItem(mechanic_fabricator.Item))
{
if (mechanic_fabricator.SelectedItem?.TargetItem.Identifier != "extinguisher")
{
mechanic_fabricator.HighlightRecipe("extinguisher", highlightColor);
}
else
{
if (mechanic_fabricator.OutputContainer.Inventory.FindItemByIdentifier("extinguisher".ToIdentifier()) != null)
{
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".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".ToIdentifier()) != null || mechanic.Inventory.FindItemByIdentifier("sodium".ToIdentifier()) != null)
{
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)
{
HighlightInventorySlot(mechanic_fabricator.InputContainer.Inventory, 0, highlightColor, .5f, .5f, 0f);
}
if (mechanic_fabricator.InputContainer.Inventory.GetItemAt(1) == null)
{
HighlightInventorySlot(mechanic_fabricator.InputContainer.Inventory, 1, highlightColor, .5f, .5f, 0f);
}
}
}
}
yield return null;
} while (mechanic.Inventory.FindItemByIdentifier("extinguisher".ToIdentifier()) == null); // Wait until extinguisher is created
RemoveCompletedObjective(5);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective5");
SetHighlight(mechanic_fabricator.Item, false);
SetDoorAccess(mechanic_fourthDoor, mechanic_fourthDoorLight, true);
// Room 5
do { yield return null; } while (!mechanic_fireSensor.MotionDetected);
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(6);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective6");
if (mechanic.HasEquippedItem("extinguisher".ToIdentifier())) // do not trigger if dropped already
{
TriggerTutorialSegment(7);
do
{
HighlightInventorySlot(mechanic.Inventory, "extinguisher".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
yield return null;
} while (mechanic.HasEquippedItem("extinguisher".ToIdentifier()));
RemoveCompletedObjective(7);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective7");
}
SetDoorAccess(mechanic_fifthDoor, mechanic_fifthDoorLight, true);
// Room 6
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Diving"), ChatMessageType.Radio, null);
do { yield return null; } while (!mechanic_divingSuitObjectiveSensor.MotionDetected);
TriggerTutorialSegment(8); // Dangers of pressure, equip diving suit objective
SetHighlight(mechanic_divingSuitContainer.Item, true);
do
{
if (IsSelectedItem(mechanic_divingSuitContainer.Item))
{
if (mechanic_divingSuitContainer.Inventory.visualSlots != null)
{
for (int i = 0; i < mechanic_divingSuitContainer.Inventory.Capacity; i++)
{
HighlightInventorySlot(mechanic_divingSuitContainer.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
}
}
}
yield return null;
} while (!mechanic.HasEquippedItem("divingsuit".ToIdentifier(), slotType: InvSlotType.OuterClothes));
SetHighlight(mechanic_divingSuitContainer.Item, false);
RemoveCompletedObjective(8);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective8");
SetDoorAccess(tutorial_mechanicFinalDoor, tutorial_mechanicFinalDoorLight, true);
// Room 7
mechanic.AddActiveObjectiveEntity(mechanic_brokenWall_2, mechanic_weldIcon, mechanic_repairIconColor);
do { yield return null; } while (WallHasDamagedSections(mechanic_brokenWall_2));
mechanic.RemoveActiveObjectiveEntity(mechanic_brokenWall_2);
yield return new WaitForSeconds(2f, false);
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>();
repairablePumpComponent.CanBeSelected = true;
do
{
yield return null;
if (repairablePumpComponent.IsBelowRepairThreshold)
{
if (!mechanic.HasEquippedItem("wrench".ToIdentifier()))
{
HighlightInventorySlot(mechanic.Inventory, "wrench".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
}
else if (IsSelectedItem(mechanic_brokenPump.Item) && repairablePumpComponent.CurrentFixer == null)
{
if (repairablePumpComponent.RepairButton.FlashTimer <= 0)
{
repairablePumpComponent.RepairButton.Flash();
}
}
}
else
{
if (IsSelectedItem(mechanic_brokenPump.Item))
{
if (mechanic_brokenPump.PowerButton.FlashTimer <= 0)
{
mechanic_brokenPump.PowerButton.Flash(uiHighlightColor, 1.5f, true);
}
}
}
} while (repairablePumpComponent.IsBelowRepairThreshold || mechanic_brokenPump.FlowPercentage >= 0 || !mechanic_brokenPump.IsActive);
RemoveCompletedObjective(9);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective9");
SetHighlight(mechanic_brokenPump.Item, false);
do { yield return null; } while (mechanic_brokenhull_2.WaterPercentage > waterVolumeBeforeOpening);
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
// Submarine
do { yield return null; } while (!tutorial_enteredSubmarineSensor.MotionDetected);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Submarine"), ChatMessageType.Radio, null);
TriggerTutorialSegment(10); // Repairing ballast pumps, engine
while (ContentRunning) yield return null;
mechanic.AddActiveObjectiveEntity(mechanic_ballastPump_1.Item, mechanic_repairIcon, mechanic_repairIconColor);
mechanic.AddActiveObjectiveEntity(mechanic_ballastPump_2.Item, mechanic_repairIcon, mechanic_repairIconColor);
mechanic.AddActiveObjectiveEntity(mechanic_submarineEngine.Item, mechanic_repairIcon, mechanic_repairIconColor);
SetHighlight(mechanic_ballastPump_1.Item, true);
SetHighlight(mechanic_ballastPump_2.Item, true);
SetHighlight(mechanic_submarineEngine.Item, true);
Repairable repairablePumpComponent1 = mechanic_ballastPump_1.Item.GetComponent<Repairable>();
Repairable repairablePumpComponent2 = mechanic_ballastPump_2.Item.GetComponent<Repairable>();
Repairable repairableEngineComponent = mechanic_submarineEngine.Item.GetComponent<Repairable>();
// 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(10);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective10");
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Complete"), ChatMessageType.Radio, null);
// END TUTORIAL
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Completed");
CoroutineManager.StartCoroutine(TutorialCompleted());
}
private bool IsSelectedItem(Item item)
{
return mechanic?.SelectedConstruction == item;
}
private bool WallHasDamagedSections(Structure wall)
{
for (int i = 0; i < wall.SectionCount; i++)
{
if (wall.Sections[i].damage > 0) return true;
}
return false;
}
private void CheckHighlights(Repairable comp1, Repairable comp2, Repairable comp3)
{
if (!comp1.IsBelowRepairThreshold && mechanic_ballastPump_1.Item.ExternalHighlight)
{
SetHighlight(mechanic_ballastPump_1.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_1.Item);
}
if (!comp2.IsBelowRepairThreshold && mechanic_ballastPump_2.Item.ExternalHighlight)
{
SetHighlight(mechanic_ballastPump_2.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_2.Item);
}
if (!comp3.IsBelowRepairThreshold && mechanic_submarineEngine.Item.ExternalHighlight)
{
SetHighlight(mechanic_submarineEngine.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_submarineEngine.Item);
}
}
}
}

View File

@@ -1,526 +0,0 @@
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Xml.Linq;
using System.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
namespace Barotrauma.Tutorials
{
class OfficerTutorial : ScenarioTutorial
{
// Other tutorial items
private LightComponent tutorial_mechanicFinalDoorLight;
private Steering tutorial_submarineSteering;
// Room 1
private float shakeTimer = 1f;
private float shakeAmount = 20f;
// Room 2
private MotionSensor officer_equipmentObjectiveSensor;
private ItemContainer officer_equipmentCabinet;
private Door officer_firstDoor;
private LightComponent officer_firstDoorLight;
// Room 3
private MotionSensor officer_crawlerSensor;
private Character officer_crawler;
private Vector2 officer_crawlerSpawnPos;
private Door officer_secondDoor;
private LightComponent officer_secondDoorLight;
// Room 4
private MotionSensor officer_somethingBigSensor;
private ItemContainer officer_coilgunLoader;
private ItemContainer officer_ammoShelf_1;
private ItemContainer officer_ammoShelf_2;
private PowerContainer officer_superCapacitor;
private Item officer_coilgunPeriscope;
private Character officer_hammerhead;
private Vector2 officer_hammerheadSpawnPos;
private Door officer_thirdDoor;
private LightComponent officer_thirdDoorLight;
// Room 5
private MotionSensor officer_rangedWeaponSensor;
private ItemContainer officer_rangedWeaponCabinet;
private ItemContainer officer_rangedWeaponHolder;
private Door officer_fourthDoor;
private LightComponent officer_fourthDoorLight;
// Room 6
private MotionSensor officer_mudraptorObjectiveSensor;
private Vector2 officer_mudraptorSpawnPos;
private Character officer_mudraptor;
private Door tutorial_securityFinalDoor;
private LightComponent tutorial_securityFinalDoorLight;
// Submarine
private Door tutorial_submarineDoor;
private LightComponent tutorial_submarineDoorLight;
private MotionSensor tutorial_enteredSubmarineSensor;
private Item officer_subAmmoBox_1;
private Item officer_subAmmoBox_2;
private ItemContainer officer_subAmmoShelf;
private ItemContainer officer_subLoader_1;
private ItemContainer officer_subLoader_2;
private PowerContainer officer_subSuperCapacitor_1;
private PowerContainer officer_subSuperCapacitor_2;
// Variables
private LocalizedString radioSpeakerName;
private Character officer;
private float superCapacitorRechargeRate = 10;
private Sprite officer_gunIcon;
private Color officer_gunIconColor;
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)));
}
protected override void Initialize()
{
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
officer = Character.Controlled;
foreach (Item item in officer.Inventory.AllItemsMod)
{
if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(officer);
officer.Inventory.RemoveItem(item);
}
var gunOrder = OrderPrefab.Prefabs["operateweapons"];
officer_gunIcon = gunOrder.SymbolSprite;
officer_gunIconColor = gunOrder.Color;
var bandage = FindOrGiveItem(officer, "antibleeding1".ToIdentifier());
bandage.Unequip(officer);
officer.Inventory.RemoveItem(bandage);
FindOrGiveItem(officer, "antibleeding1".ToIdentifier());
// Other tutorial items
tutorial_mechanicFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoorlight")).GetComponent<LightComponent>();
tutorial_submarineSteering = Item.ItemList.Find(i => i.HasTag("command")).GetComponent<Steering>();
tutorial_submarineSteering.CanBeSelected = false;
foreach (ItemComponent ic in tutorial_submarineSteering.Item.Components)
{
ic.CanBeSelected = false;
}
SetDoorAccess(null, tutorial_mechanicFinalDoorLight, false);
// Room 2
officer_equipmentObjectiveSensor = Item.ItemList.Find(i => i.HasTag("officer_equipmentobjectivesensor")).GetComponent<MotionSensor>();
officer_equipmentCabinet = Item.ItemList.Find(i => i.HasTag("officer_equipmentcabinet")).GetComponent<ItemContainer>();
officer_firstDoor = Item.ItemList.Find(i => i.HasTag("officer_firstdoor")).GetComponent<Door>();
officer_firstDoorLight = Item.ItemList.Find(i => i.HasTag("officer_firstdoorlight")).GetComponent<LightComponent>();
SetDoorAccess(officer_firstDoor, officer_firstDoorLight, false);
// Room 3
officer_crawlerSensor = Item.ItemList.Find(i => i.HasTag("officer_crawlerobjectivesensor")).GetComponent<MotionSensor>();
officer_crawlerSpawnPos = Item.ItemList.Find(i => i.HasTag("officer_crawlerspawn")).WorldPosition;
officer_secondDoor = Item.ItemList.Find(i => i.HasTag("officer_seconddoor")).GetComponent<Door>();
officer_secondDoorLight = Item.ItemList.Find(i => i.HasTag("officer_seconddoorlight")).GetComponent<LightComponent>();
SetDoorAccess(officer_secondDoor, officer_secondDoorLight, false);
// Room 4
officer_somethingBigSensor = Item.ItemList.Find(i => i.HasTag("officer_somethingbigobjectivesensor")).GetComponent<MotionSensor>();
officer_coilgunLoader = Item.ItemList.Find(i => i.HasTag("officer_coilgunloader")).GetComponent<ItemContainer>();
officer_superCapacitor = Item.ItemList.Find(i => i.HasTag("officer_supercapacitor")).GetComponent<PowerContainer>();
officer_coilgunPeriscope = Item.ItemList.Find(i => i.HasTag("officer_coilgunperiscope"));
officer_hammerheadSpawnPos = Item.ItemList.Find(i => i.HasTag("officer_hammerheadspawn")).WorldPosition;
officer_thirdDoor = Item.ItemList.Find(i => i.HasTag("officer_thirddoor")).GetComponent<Door>();
officer_thirdDoorLight = Item.ItemList.Find(i => i.HasTag("officer_thirddoorlight")).GetComponent<LightComponent>();
officer_ammoShelf_1 = Item.ItemList.Find(i => i.HasTag("officer_ammoshelf_1")).GetComponent<ItemContainer>();
officer_ammoShelf_2 = Item.ItemList.Find(i => i.HasTag("officer_ammoshelf_2")).GetComponent<ItemContainer>();
SetDoorAccess(officer_thirdDoor, officer_thirdDoorLight, false);
// Room 5
officer_rangedWeaponSensor = Item.ItemList.Find(i => i.HasTag("officer_rangedweaponobjectivesensor")).GetComponent<MotionSensor>();
officer_rangedWeaponCabinet = Item.ItemList.Find(i => i.HasTag("officer_rangedweaponcabinet")).GetComponent<ItemContainer>();
officer_rangedWeaponHolder = Item.ItemList.Find(i => i.HasTag("officer_rangedweaponholder")).GetComponent<ItemContainer>();
officer_fourthDoor = Item.ItemList.Find(i => i.HasTag("officer_fourthdoor")).GetComponent<Door>();
officer_fourthDoorLight = Item.ItemList.Find(i => i.HasTag("officer_fourthdoorlight")).GetComponent<LightComponent>();
SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, false);
// Room 6
officer_mudraptorObjectiveSensor = Item.ItemList.Find(i => i.HasTag("officer_mudraptorobjectivesensor")).GetComponent<MotionSensor>();
officer_mudraptorSpawnPos = Item.ItemList.Find(i => i.HasTag("officer_mudraptorspawn")).WorldPosition;
tutorial_securityFinalDoor = Item.ItemList.Find(i => i.HasTag("tutorial_securityfinaldoor")).GetComponent<Door>();
tutorial_securityFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_securityfinaldoorlight")).GetComponent<LightComponent>();
SetDoorAccess(tutorial_securityFinalDoor, tutorial_securityFinalDoorLight, false);
// Submarine
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
tutorial_enteredSubmarineSensor = Item.ItemList.Find(i => i.HasTag("tutorial_enteredsubmarinesensor")).GetComponent<MotionSensor>();
officer_subAmmoBox_1 = Item.ItemList.Find(i => i.HasTag("officer_subammobox_1"));
officer_subAmmoBox_2 = Item.ItemList.Find(i => i.HasTag("officer_subammobox_2"));
officer_subLoader_1 = Item.ItemList.Find(i => i.HasTag("officer_subloader_1")).GetComponent<ItemContainer>();
officer_subLoader_2 = Item.ItemList.Find(i => i.HasTag("officer_subloader_2")).GetComponent<ItemContainer>();
officer_subSuperCapacitor_1 = Item.ItemList.Find(i => i.HasTag("officer_subsupercapacitor_1")).GetComponent<PowerContainer>();
officer_subSuperCapacitor_2 = Item.ItemList.Find(i => i.HasTag("officer_subsupercapacitor_2")).GetComponent<PowerContainer>();
officer_subAmmoShelf = Item.ItemList.Find(i => i.HasTag("officer_subammoshelf")).GetComponent<ItemContainer>();
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Started");
GameAnalyticsManager.AddDesignEvent("Tutorial:Started");
}
public override IEnumerable<CoroutineStatus> UpdateState()
{
while (GameMain.Instance.LoadingScreenOpen) yield return null;
yield return new WaitForSeconds(0.01f);
// Room 1
SoundPlayer.PlayDamageSound("StructureBlunt", 10, Character.Controlled.WorldPosition);
while (shakeTimer > 0.0f) // Wake up, shake
{
shakeTimer -= 0.1f;
GameMain.GameScreen.Cam.Shake = shakeAmount;
yield return new WaitForSeconds(0.1f, false);
}
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.WakeUp"), ChatMessageType.Radio, null);
// Room 2
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, 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;
bool thirdSlotRemoved = false;
do
{
if (IsSelectedItem(officer_equipmentCabinet.Item))
{
if (!firstSlotRemoved)
{
HighlightInventorySlot(officer_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f);
if (officer_equipmentCabinet.Inventory.GetItemAt(0) == null) { firstSlotRemoved = true; }
}
if (!secondSlotRemoved)
{
HighlightInventorySlot(officer_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f);
if (officer_equipmentCabinet.Inventory.GetItemAt(1) == null) { secondSlotRemoved = true; }
}
if (!thirdSlotRemoved)
{
HighlightInventorySlot(officer_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f);
if (officer_equipmentCabinet.Inventory.GetItemAt(2) == null) { thirdSlotRemoved = true; }
}
for (int i = 0; i < officer.Inventory.visualSlots.Length; i++)
{
if (officer.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(officer.Inventory, i, highlightColor, .5f, .5f, 0f); }
}
}
yield return null;
} while (!officer_equipmentCabinet.Inventory.IsEmpty()); // Wait until looted
//RemoveCompletedObjective(segments[0]);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Objective0");
SetHighlight(officer_equipmentCabinet.Item, false);
do { yield return null; } while (IsSelectedItem(officer_equipmentCabinet.Item));
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot)); // Equip melee weapon & armor
do
{
if (!officer.HasEquippedItem("stunbaton".ToIdentifier()))
{
HighlightInventorySlot(officer.Inventory, "stunbaton".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
if (!officer.HasEquippedItem("bodyarmor".ToIdentifier()))
{
HighlightInventorySlot(officer.Inventory, "bodyarmor".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
if (!officer.HasEquippedItem("ballistichelmet1".ToIdentifier()))
{
HighlightInventorySlot(officer.Inventory, "ballistichelmet1".ToIdentifier(), highlightColor, .5f, .5f, 0f);
}
yield return new WaitForSeconds(1f, false);
} while (!officer.HasEquippedItem("stunbaton".ToIdentifier()) || !officer.HasEquippedItem("bodyarmor".ToIdentifier()) || !officer.HasEquippedItem("ballistichelmet1".ToIdentifier()));
RemoveCompletedObjective(1);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Objective1");
SetDoorAccess(officer_firstDoor, officer_firstDoorLight, true);
// Room 3
do { yield return null; } while (!officer_crawlerSensor.MotionDetected);
TriggerTutorialSegment(2);
officer_crawler = SpawnMonster("crawler", officer_crawlerSpawnPos);
do { yield return null; } while (!officer_crawler.IsDead);
RemoveCompletedObjective(2);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Objective2");
Heal(officer);
yield return new WaitForSeconds(1f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.CrawlerDead"), ChatMessageType.Radio, null);
SetDoorAccess(officer_secondDoor, officer_secondDoorLight, true);
// Room 4
do { yield return null; } while (!officer_somethingBigSensor.MotionDetected);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.SomethingBig"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(3); // Arm coilgun
do
{
SetHighlight(officer_coilgunLoader.Item, officer_coilgunLoader.Inventory.GetItemAt(0) == null || officer_coilgunLoader.Inventory.GetItemAt(0).Condition == 0);
HighlightInventorySlot(officer_coilgunLoader.Inventory, 0, highlightColor, .5f, .5f, 0f);
SetHighlight(officer_superCapacitor.Item, officer_superCapacitor.RechargeSpeed < superCapacitorRechargeRate);
SetHighlight(officer_ammoShelf_1.Item, officer_coilgunLoader.Item.ExternalHighlight );
SetHighlight(officer_ammoShelf_2.Item, officer_coilgunLoader.Item.ExternalHighlight );
if (IsSelectedItem(officer_coilgunLoader.Item))
{
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);
SetHighlight(officer_coilgunLoader.Item, false);
SetHighlight(officer_superCapacitor.Item, false);
SetHighlight(officer_ammoShelf_1.Item, false);
SetHighlight(officer_ammoShelf_2.Item, false);
RemoveCompletedObjective(3);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Objective3");
yield return new WaitForSeconds(2f, false);
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;
officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
SetHighlight(officer_coilgunPeriscope, true);
float originalDistance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerheadSpawnPos);
do
{
float distance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerhead.WorldPosition);
if (distance > originalDistance * 1.5f || officer_hammerhead.WorldPosition.Y > officer_coilgunPeriscope.WorldPosition.Y)
{
// Don't let the Hammerhead go too far.
officer_hammerhead.TeleportTo(officer_hammerheadSpawnPos + new Vector2(0, -1000));
}
if (distance > originalDistance)
{
// Ensure that the Hammerhead targets the player
officer.AiTarget.SoundRange = float.MaxValue;
officer.AiTarget.SightRange = float.MaxValue;
officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
if ((officer_hammerhead.AIController as EnemyAIController)?.SelectedTargetingParams != null)
{
((EnemyAIController)officer_hammerhead.AIController).SelectedTargetingParams.ReactDistance = 5000.0f;
}
/*var ai = officer_hammerhead.AIController as EnemyAIController;
ai.sight = 2.0f;*/
}
yield return null;
}
while(!officer_hammerhead.IsDead);
Heal(officer);
SetHighlight(officer_coilgunPeriscope, false);
RemoveCompletedObjective(4);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Objective4");
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);
// Room 5
//do { yield return null; } while (!officer_rangedWeaponSensor.MotionDetected);
do { yield return null; } while (!officer_thirdDoor.IsOpen);
yield return new WaitForSeconds(3f, false);
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".ToIdentifier(), highlightColor, 0.5f, 0.5f, 0f);
yield return null;
} while (!officer.HasEquippedItem("shotgun".ToIdentifier())); // Wait until equipped
ItemContainer shotGunChamber = officer.Inventory.FindItemByIdentifier("shotgun".ToIdentifier()).GetComponent<ItemContainer>();
SetHighlight(officer_rangedWeaponCabinet.Item, true);
do
{
if (IsSelectedItem(officer_rangedWeaponCabinet.Item))
{
if (officer_rangedWeaponCabinet.Inventory.visualSlots != null)
{
for (int i = 0; i < officer_rangedWeaponCabinet.Inventory.Capacity; i++)
{
if (officer_rangedWeaponCabinet.Inventory.GetItemAt(i)?.Prefab.Identifier == "shotgunshell")
{
HighlightInventorySlot(officer_rangedWeaponCabinet.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
}
}
}
}
for (int i = 0; i < officer.Inventory.Capacity; i++)
{
if (officer.Inventory.GetItemAt(i)?.Prefab.Identifier == "shotgunshell")
{
HighlightInventorySlot(officer.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
}
}
if (officer.Inventory.FindItemByIdentifier("shotgunshell".ToIdentifier()) != null || (IsSelectedItem(officer_rangedWeaponCabinet.Item) && officer_rangedWeaponCabinet.Inventory.FindItemByIdentifier("shotgunshell".ToIdentifier()) != null))
{
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(5);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Objective5");
SetHighlight(officer_rangedWeaponCabinet.Item, false);
SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true);
// Room 6
do { yield return null; } while (!officer_mudraptorObjectiveSensor.MotionDetected);
TriggerTutorialSegment(6);
officer_mudraptor = SpawnMonster("mudraptor", officer_mudraptorSpawnPos);
do { yield return null; } while (!officer_mudraptor.IsDead);
Heal(officer);
RemoveCompletedObjective(6);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Objective6");
SetDoorAccess(tutorial_securityFinalDoor, tutorial_securityFinalDoorLight, true);
// Submarine
do { yield return null; } while (!tutorial_enteredSubmarineSensor.MotionDetected);
TriggerTutorialSegment(7);
while (ContentRunning) yield return null;
officer.AddActiveObjectiveEntity(officer_subAmmoBox_1, officer_gunIcon, officer_gunIconColor);
officer.AddActiveObjectiveEntity(officer_subAmmoBox_2, officer_gunIcon, officer_gunIconColor);
officer.AddActiveObjectiveEntity(officer_subSuperCapacitor_1.Item, officer_gunIcon, officer_gunIconColor);
officer.AddActiveObjectiveEntity(officer_subSuperCapacitor_2.Item, officer_gunIcon, officer_gunIconColor);
SetHighlight(officer_subSuperCapacitor_1.Item, true);
SetHighlight(officer_subSuperCapacitor_2.Item, true);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.Submarine"), ChatMessageType.Radio, null);
do
{
SetHighlight(officer_subLoader_1.Item, officer_subLoader_1.Inventory.GetItemAt(0) == null || officer_subLoader_1.Inventory.GetItemAt(0).Condition == 0);
SetHighlight(officer_subLoader_2.Item, officer_subLoader_2.Inventory.GetItemAt(0) == null || officer_subLoader_2.Inventory.GetItemAt(0).Condition == 0);
HighlightInventorySlot(officer_subLoader_1.Inventory, 0, highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(officer_subLoader_2.Inventory, 0, highlightColor, .5f, .5f, 0f);
if (officer_subSuperCapacitor_1.Item.ExternalHighlight && officer_subSuperCapacitor_1.RechargeSpeed >= superCapacitorRechargeRate)
{
SetHighlight(officer_subSuperCapacitor_1.Item, false);
officer.RemoveActiveObjectiveEntity(officer_subSuperCapacitor_1.Item);
}
if (officer_subSuperCapacitor_2.Item.ExternalHighlight && officer_subSuperCapacitor_2.RechargeSpeed >= superCapacitorRechargeRate)
{
SetHighlight(officer_subSuperCapacitor_2.Item, false);
officer.RemoveActiveObjectiveEntity(officer_subSuperCapacitor_2.Item);
}
SetHighlight(officer_subAmmoBox_1, officer_subAmmoBox_1.ParentInventory != officer_subLoader_1.Inventory && officer_subAmmoBox_1.ParentInventory != officer_subLoader_2.Inventory);
SetHighlight(officer_subAmmoBox_2, officer_subAmmoBox_2.ParentInventory != officer_subLoader_1.Inventory && officer_subAmmoBox_2.ParentInventory != officer_subLoader_2.Inventory);
SetHighlight(officer_subAmmoShelf.Item, officer_subLoader_1.Item.ExternalHighlight || officer_subLoader_2.Item.ExternalHighlight);
if (officer_subAmmoBox_1.ParentInventory == officer_subLoader_1.Inventory || officer_subAmmoBox_1.ParentInventory == officer_subLoader_2.Inventory) officer.RemoveActiveObjectiveEntity(officer_subAmmoBox_1);
if (officer_subAmmoBox_2.ParentInventory == officer_subLoader_1.Inventory || officer_subAmmoBox_2.ParentInventory == officer_subLoader_2.Inventory) officer.RemoveActiveObjectiveEntity(officer_subAmmoBox_2);
yield return null;
} while (officer_subLoader_1.Item.ExternalHighlight || officer_subLoader_2.Item.ExternalHighlight || officer_subSuperCapacitor_1.Item.ExternalHighlight || officer_subSuperCapacitor_2.Item.ExternalHighlight);
SetHighlight(officer_subLoader_1.Item, false);
SetHighlight(officer_subLoader_2.Item, false);
SetHighlight(officer_subSuperCapacitor_1.Item, false);
SetHighlight(officer_subSuperCapacitor_2.Item, false);
SetHighlight(officer_subAmmoBox_1, false);
SetHighlight(officer_subAmmoBox_2, false);
SetHighlight(officer_subAmmoShelf.Item, false);
officer.RemoveActiveObjectiveEntity(officer_subSuperCapacitor_1.Item);
officer.RemoveActiveObjectiveEntity(officer_subSuperCapacitor_2.Item);
officer.RemoveActiveObjectiveEntity(officer_subAmmoBox_1);
officer.RemoveActiveObjectiveEntity(officer_subAmmoBox_2);
RemoveCompletedObjective(7);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Objective7");
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.Complete"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(4f, false);
GameAnalyticsManager.AddDesignEvent("Tutorial:OfficerTutorial:Completed");
CoroutineManager.StartCoroutine(TutorialCompleted());
}
private bool IsSelectedItem(Item item)
{
return officer?.SelectedConstruction == item;
}
private Character SpawnMonster(string speciesName, Vector2 pos)
{
var character = Character.Create(speciesName, pos, ToolBox.RandomSeed(8));
var ai = character.AIController as EnemyAIController;
ai.TargetOutposts = true;
character.CharacterHealth.SetVitality(character.Health / 2);
character.AnimController.Limbs.Where(l => l.attack != null).Select(l => l.attack).ForEach(a => a.AfterAttack = AIBehaviorAfterAttack.FallBack);
return character;
}
}
}

View File

@@ -1,307 +0,0 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Tutorials
{
abstract class ScenarioTutorial : Tutorial
{
private CoroutineHandle tutorialCoroutine;
private Character character;
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;
private bool currentTutorialCompleted = false;
private float fadeOutTime = 3f;
protected float waitBeforeFade = 4f;
// Colors
protected Color highlightColor = Color.OrangeRed;
protected Color uiHighlightColor = new Color(150, 50, 0);
protected Color buttonHighlightColor = new Color(255, 100, 0);
protected Color inaccessibleColor = GUIStyle.Red;
protected Color accessibleColor = GUIStyle.Green;
protected ScenarioTutorial(Identifier identifier, params Segment[] segments) : base(identifier, segments) { }
protected abstract void Initialize();
protected override IEnumerable<CoroutineStatus> Loading()
{
SubmarineInfo subInfo = new SubmarineInfo(submarinePath);
LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Identifier == levelParams);
yield return CoroutineStatus.Running;
GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefabs: null);
(GameMain.GameSession.GameMode as TutorialMode).Tutorial = this;
if (generationParams != null)
{
Biome biome =
Biome.Prefabs.FirstOrDefault(b => generationParams.AllowedBiomeIdentifiers.Contains(b.Identifier)) ??
Biome.Prefabs.First();
if (!string.IsNullOrEmpty(startOutpostPath))
{
startOutpost = new SubmarineInfo(startOutpostPath);
}
/*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);
}
else
{
GameMain.GameSession.StartRound(levelSeed);
}
GameMain.GameSession.EventManager.ActiveEvents.Clear();
GameMain.GameSession.EventManager.Enabled = false;
GameMain.GameScreen.Select();
Submarine.MainSub.GodMode = true;
foreach (Structure wall in Structure.WallList)
{
if (wall.Submarine != null && wall.Submarine.Info.IsOutpost)
{
wall.Indestructible = true;
}
}
CharacterInfo charInfo = GetCharacterInfo();
WayPoint wayPoint = GetSpawnPoint(charInfo);
if (wayPoint == null)
{
DebugConsole.ThrowError("A waypoint with the spawntype \"" + spawnPointType + "\" is required for the tutorial event");
yield return CoroutineStatus.Failure;
yield break;
}
character = Character.Create(charInfo, wayPoint.WorldPosition, "", isRemotePlayer: false, hasAi: false);
character.TeamID = CharacterTeamType.Team1;
Character.Controlled = character;
character.GiveJobItems(null);
var idCard = character.Inventory.FindItemByTag("identitycard".ToIdentifier());
if (idCard == null)
{
DebugConsole.ThrowError("Item prefab \"ID Card\" not found!");
yield return CoroutineStatus.Failure;
yield break;
}
idCard.AddTag("com");
idCard.AddTag("eng");
foreach (Item item in Item.ItemList)
{
Door door = item.GetComponent<Door>();
if (door != null)
{
door.CanBeWelded = false;
}
}
tutorialCoroutine = CoroutineManager.StartCoroutine(UpdateState());
Initialize();
yield return CoroutineStatus.Success;
}
protected abstract CharacterInfo GetCharacterInfo();
public override void AddToGUIUpdateList()
{
if (!currentTutorialCompleted)
{
base.AddToGUIUpdateList();
}
}
private WayPoint GetSpawnPoint(CharacterInfo charInfo)
{
/*Submarine spawnSub = null;
if (this.spawnSub != string.Empty)
{
switch (this.spawnSub)
{
case "startoutpost":
spawnSub = Level.Loaded.StartOutpost;
break;
case "endoutpost":
spawnSub = Level.Loaded.EndOutpost;
break;
default:
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?.Identifier == identifier)
{
if (option == null)
{
return true;
}
else
{
return currentOrderInfo?.Option == option;
}
}
return false;
}
protected void SetHighlight(Item item, bool state)
{
if (item.ExternalHighlight == state) return;
item.SpriteColor = (state) ? highlightColor : Color.White;
item.ExternalHighlight = state;
}
protected void SetHighlight(Structure structure, bool state)
{
structure.SpriteColor = (state) ? highlightColor : Color.White;
structure.ExternalHighlight = state;
}
protected void SetHighlight(Character character, bool state)
{
character.ExternalHighlight = state;
}
protected void SetDoorAccess(Door door, LightComponent light, bool state)
{
if (state && door != null) door.requiredItems.Clear();
if (light != null) light.LightColor = (state) ? accessibleColor : inaccessibleColor;
}
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (character != null)
{
if (character.Oxygen < 1)
{
character.Oxygen = 1;
}
if (character.IsDead)
{
CoroutineManager.StartCoroutine(Dead());
}
else if (Character.Controlled == null)
{
if (tutorialCoroutine != null)
{
CoroutineManager.StopCoroutines(tutorialCoroutine);
}
GUI.PreventPauseMenuToggle = false;
ContentRunning = false;
infoBox = null;
}
else if (Character.Controlled.IsDead)
{
CoroutineManager.StartCoroutine(Dead());
}
}
}
public override void Stop()
{
if (tutorialCoroutine != null)
{
CoroutineManager.StopCoroutines(tutorialCoroutine);
}
base.Stop();
}
private IEnumerable<CoroutineStatus> Dead()
{
GUI.PreventPauseMenuToggle = true;
Character.Controlled = character = null;
Stop();
GameAnalyticsManager.AddDesignEvent("Tutorial:Died");
yield return new WaitForSeconds(3.0f);
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;
messageBox.Buttons[1].OnClicked = GameMain.MainMenuScreen.ReturnToMainMenu;
messageBox.Buttons[1].OnClicked += messageBox.Close;
yield return CoroutineStatus.Success;
}
protected IEnumerable<CoroutineStatus> TutorialCompleted()
{
GUI.PreventPauseMenuToggle = true;
Character.Controlled.ClearInputs();
Character.Controlled = null;
GameAnalyticsManager.AddDesignEvent("Tutorial:Completed");
yield return new WaitForSeconds(waitBeforeFade);
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: fadeOutTime);
currentTutorialCompleted = Completed = true;
while (endCinematic.Running) yield return null;
Stop();
GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
}
protected void Heal(Character character)
{
character.SetAllDamage(0.0f, 0.0f, 0.0f);
character.Oxygen = 100.0f;
character.Bloodloss = 0.0f;
character.SetStun(0.0f, true);
}
protected Item FindOrGiveItem(Character character, Identifier identifier)
{
var item = character.Inventory.FindItemByIdentifier(identifier);
if (item != null && !item.Removed) { return item; }
ItemPrefab itemPrefab = MapEntityPrefab.Find(name: null, identifier: identifier) as ItemPrefab;
item = new Item(itemPrefab, Vector2.Zero, submarine: null);
character.Inventory.TryPutItem(item, character, item.AllowedSlots);
return item;
}
}
}

View File

@@ -6,10 +6,7 @@ namespace Barotrauma
{
public Tutorial Tutorial;
public TutorialMode(GameModePreset preset)
: base(preset)
{
}
public TutorialMode(GameModePreset preset) : base(preset) { }
public override void Start()
{
@@ -31,7 +28,7 @@ namespace Barotrauma
public override void Update(float deltaTime)
{
base.Update(deltaTime);
Tutorial.Update(deltaTime);
Tutorial.Update();
}
}
}

View File

@@ -14,6 +14,7 @@ namespace Barotrauma
public static bool IsTabMenuOpen => GameMain.GameSession?.tabMenu != null;
public static TabMenu TabMenuInstance => GameMain.GameSession?.tabMenu;
private float prevHudScale;
private TabMenu tabMenu;
@@ -119,6 +120,7 @@ namespace Barotrauma
return true;
}
};
prevHudScale = GameSettings.CurrentConfig.Graphics.HUDScale;
}
public void AddToGUIUpdateList()
@@ -178,6 +180,12 @@ namespace Barotrauma
}
}
public void HUDScaleChanged()
{
CreateTopLeftButtons();
GameMode?.HUDScaleChanged();
}
partial void UpdateProjSpecific(float deltaTime)
{
if (GUI.DisableHUD) { return; }

View File

@@ -112,19 +112,19 @@ namespace Barotrauma
CheckReminders();
}
public static void OnSetSelectedConstruction(Character character, Item oldConstruction, Item newConstruction)
public static void OnSetSelectedItem(Character character, Item oldItem, Item newItem)
{
if (oldConstruction == newConstruction) { return; }
if (oldItem == newItem) { return; }
if (Character.Controlled != null && Character.Controlled == character && oldConstruction != null && oldConstruction.GetComponent<Ladder>() == null)
if (Character.Controlled != null && Character.Controlled == character && oldItem != null && !oldItem.IsLadder)
{
TimeStoppedInteracting = Timing.TotalTime;
}
if (newConstruction == null) { return; }
if (newConstruction.GetComponent<Ladder>() != null) { return; }
if (newConstruction.GetComponent<ConnectionPanel>() is ConnectionPanel cp && cp.User == character) { return; }
OnStartedInteracting(character, newConstruction);
if (newItem == null) { return; }
if (newItem.IsLadder) { return; }
if (newItem.GetComponent<ConnectionPanel>() is ConnectionPanel cp && cp.User == character) { return; }
OnStartedInteracting(character, newItem);
}
private static void OnStartedInteracting(Character character, Item item)
@@ -177,10 +177,10 @@ namespace Barotrauma
private static void CheckIsInteracting()
{
if (!CanDisplayHints()) { return; }
if (Character.Controlled?.SelectedConstruction == null) { return; }
if (Character.Controlled?.SelectedItem == null) { return; }
if (Character.Controlled.SelectedConstruction.GetComponent<Reactor>() is Reactor reactor && reactor.PowerOn &&
Character.Controlled.SelectedConstruction.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
if (Character.Controlled.SelectedItem.GetComponent<Reactor>() is Reactor reactor && reactor.PowerOn &&
Character.Controlled.SelectedItem.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
containedItems.Count(i => i.HasTag("reactorfuel")) > 1)
{
if (DisplayHint("onisinteracting.reactorwithextrarods".ToIdentifier())) { return; }
@@ -272,7 +272,7 @@ namespace Barotrauma
if (!CanDisplayHints()) { return; }
if (sonar == null || sonar.Removed) { return; }
if (spottedCharacter == null || spottedCharacter.Removed || spottedCharacter.IsDead) { return; }
if (Character.Controlled.SelectedConstruction != sonar) { return; }
if (Character.Controlled.SelectedItem != sonar) { return; }
if (HumanAIController.IsFriendly(Character.Controlled, spottedCharacter)) { return; }
DisplayHint("onsonarspottedenemy".ToIdentifier());
}
@@ -305,7 +305,7 @@ namespace Barotrauma
{
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled) { return; }
if (character.SelectedConstruction != null || character.FocusedItem != null) { return; }
if (character.HasSelectedAnyItem || character.FocusedItem != null) { return; }
if (item == null || !item.IsShootable || !item.RequireAimToUse) { return; }
if (TimeStoppedInteracting + 1 > Timing.TotalTime) { return; }
if (GUI.MouseOn != null) { return; }
@@ -317,7 +317,7 @@ namespace Barotrauma
variables: new[] { ("[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim)) },
onUpdate: () =>
{
if (character.SelectedConstruction == null && GUI.MouseOn == null && PlayerInput.KeyDown(InputType.Aim))
if (character.SelectedItem == null && GUI.MouseOn == null && PlayerInput.KeyDown(InputType.Aim))
{
ActiveHintMessageBox.Close();
}

View File

@@ -354,14 +354,14 @@ namespace Barotrauma
private static IWriteMessage StartSending()
{
IWriteMessage writeMessage = new WriteOnlyMessage();
writeMessage.Write((byte)ClientPacketHeader.MEDICAL);
writeMessage.WriteByte((byte)ClientPacketHeader.MEDICAL);
return writeMessage;
}
private static void ClientSend(INetSerializableStruct? netStruct, NetworkHeader header, DeliveryMethod deliveryMethod)
{
IWriteMessage msg = StartSending();
msg.Write((byte)header);
msg.WriteByte((byte)header);
netStruct?.Write(msg);
GameMain.Client.ClientPeer?.Send(msg, deliveryMethod);
}

View File

@@ -83,7 +83,7 @@ namespace Barotrauma
foreach (var (id, _) in Clients)
{
Client? client = GameMain.Client.ConnectedClients.FirstOrDefault(c => c.ID == id);
Client? client = GameMain.Client.ConnectedClients.FirstOrDefault(c => c.SessionId == id);
GUIFrame container = new GUIFrame(new RectTransform(new Vector2(1f, 0.15f), listBox.Content.RectTransform), style: "ListBoxElement") { UserData = id };
GUILayoutGroup frame = new GUILayoutGroup(new RectTransform(Vector2.One, container.RectTransform), isHorizontal: true) { Stretch = true };
@@ -93,7 +93,7 @@ namespace Barotrauma
if (client == null)
{
string list = GameMain.Client.ConnectedClients.Aggregate("Available clients:\n", (current, c) => current + $"{c.ID}: {c.Name}\n");
string list = GameMain.Client.ConnectedClients.Aggregate("Available clients:\n", (current, c) => current + $"{c.SessionId}: {c.Name}\n");
DebugConsole.ThrowError($"Client ID {id} was reported in ready check but was not found.\n" + list.TrimEnd('\n'));
}
@@ -139,9 +139,9 @@ namespace Barotrauma
public static void ClientRead(IReadMessage inc)
{
ReadyCheckState state = (ReadyCheckState) inc.ReadByte();
ReadyCheckState state = (ReadyCheckState)inc.ReadByte();
CrewManager? crewManager = GameMain.GameSession?.CrewManager;
List<Client> otherClients = GameMain.Client.ConnectedClients;
var otherClients = GameMain.Client.ConnectedClients;
if (crewManager == null || otherClients == null)
{
if (state == ReadyCheckState.Start)
@@ -165,7 +165,7 @@ namespace Barotrauma
if (hasAuthor)
{
authorId = inc.ReadByte();
isOwn = authorId == GameMain.Client.ID;
isOwn = authorId == GameMain.Client.SessionId;
}
ushort clientCount = inc.ReadUInt16();
@@ -196,7 +196,7 @@ namespace Barotrauma
}
break;
case ReadyCheckState.Update:
ReadyStatus newState = (ReadyStatus) inc.ReadByte();
ReadyStatus newState = (ReadyStatus)inc.ReadByte();
byte targetId = inc.ReadByte();
if (crewManager.ActiveReadyCheck != null)
{
@@ -208,7 +208,7 @@ namespace Barotrauma
for (int i = 0; i < count; i++)
{
byte id = inc.ReadByte();
ReadyStatus status = (ReadyStatus) inc.ReadByte();
ReadyStatus status = (ReadyStatus)inc.ReadByte();
crewManager.ActiveReadyCheck?.UpdateState(id, status);
}
@@ -269,9 +269,9 @@ namespace Barotrauma
private static void SendState(ReadyStatus status)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ClientPacketHeader.READY_CHECK);
msg.Write((byte) ReadyCheckState.Update);
msg.Write((byte) status);
msg.WriteByte((byte)ClientPacketHeader.READY_CHECK);
msg.WriteByte((byte)ReadyCheckState.Update);
msg.WriteByte((byte)status);
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
@@ -283,8 +283,8 @@ namespace Barotrauma
ReadyCheckCooldown = DateTime.Now.AddMinutes(1);
#endif
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ClientPacketHeader.READY_CHECK);
msg.Write((byte) ReadyCheckState.Start);
msg.WriteByte((byte)ClientPacketHeader.READY_CHECK);
msg.WriteByte((byte)ReadyCheckState.Start);
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
return;
}

View File

@@ -562,7 +562,7 @@ namespace Barotrauma
break;
}
//if putting an item to a container with a max stack size of 1, only put one item from the stack
if (quickUseAction == QuickUseAction.PutToContainer && (character.SelectedConstruction?.GetComponent<ItemContainer>()?.MaxStackSize ?? 0) <= 1)
if (quickUseAction == QuickUseAction.PutToContainer && (character.SelectedItem?.GetComponent<ItemContainer>()?.MaxStackSize ?? 0) <= 1)
{
break;
}
@@ -595,14 +595,14 @@ namespace Barotrauma
if (rootInventory != null &&
rootInventory.Owner != Character.Controlled &&
rootInventory.Owner != Character.Controlled.SelectedConstruction &&
rootInventory.Owner != Character.Controlled.SelectedItem &&
rootInventory.Owner != Character.Controlled.SelectedCharacter)
{
//allow interacting if the container is linked to the item the character is interacting with
if (!(rootContainer != null &&
rootContainer.DisplaySideBySideWhenLinked &&
Character.Controlled.SelectedConstruction != null &&
rootContainer.linkedTo.Contains(Character.Controlled.SelectedConstruction)))
Character.Controlled.SelectedItem != null &&
rootContainer.linkedTo.Contains(Character.Controlled.SelectedItem)))
{
DraggingItems.Clear();
}
@@ -756,7 +756,7 @@ namespace Barotrauma
}
else
{
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
var selectedContainer = character.SelectedItem?.GetComponent<ItemContainer>();
if (selectedContainer != null &&
selectedContainer.Inventory != null &&
!selectedContainer.Inventory.Locked)
@@ -775,7 +775,8 @@ namespace Barotrauma
else
{
bool isEquippable = item.AllowedSlots.Any(s => s != InvSlotType.Any);
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
var selectedContainer = character.SelectedItem?.GetComponent<ItemContainer>();
if (selectedContainer != null &&
selectedContainer.Inventory != null &&
!selectedContainer.Inventory.Locked &&
@@ -930,7 +931,7 @@ namespace Barotrauma
}
break;
case QuickUseAction.PutToContainer:
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
var selectedContainer = character.SelectedItem?.GetComponent<ItemContainer>();
if (selectedContainer != null && selectedContainer.Inventory != null)
{
//player has selected the inventory of another item -> attempt to move the item there
@@ -965,8 +966,8 @@ namespace Barotrauma
}
break;
case QuickUseAction.PutToEquippedItem:
foreach (Item heldItem in character.HeldItems)
//order by the condition of the contained item to prefer putting into the item with the emptiest ammo/battery/tank
foreach (Item heldItem in character.HeldItems.OrderBy(it => it.ContainedItems.FirstOrDefault()?.Condition ?? 0.0f))
{
if (heldItem.OwnInventory == null) { continue; }
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items

View File

@@ -4,6 +4,7 @@ using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
namespace Barotrauma.Items.Components
{
@@ -177,7 +178,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
Color color = item.SpriteColor;
Color color = item.GetSpriteColor(withHighlight: true);
if (brokenSprite == null)
{
//broken doors turn black if no broken sprite has been configured
@@ -220,11 +221,15 @@ namespace Barotrauma.Items.Components
color, 0.0f, doorSprite.Origin, item.Scale, item.SpriteEffects, doorSprite.Depth);
}
if (brokenSprite != null && item.Health < item.MaxCondition)
float maxCondition = item.Repairables.Any() ?
item.Repairables.Min(r => r.RepairThreshold) / 100.0f * item.MaxCondition :
item.MaxCondition;
float healthRatio = item.Health / maxCondition;
if (brokenSprite != null && healthRatio < 1.0f)
{
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - item.Health / item.MaxCondition) : Vector2.One;
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - healthRatio) : Vector2.One;
if (IsHorizontal) { scale.X = 1; } else { scale.Y = 1; }
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f;
float alpha = fadeBrokenSprite ? 1.0f - healthRatio : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos,
getSourceRect(brokenSprite, openState, IsHorizontal),
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, item.SpriteEffects,

View File

@@ -166,10 +166,10 @@ namespace Barotrauma.Items.Components
List<VineTile> tiles = new List<VineTile>();
for (int i = 0; i < vineCount; i++)
{
VineTileType vineType = (VineTileType) msg.ReadRangedInteger(0b0000, 0b1111);
VineTileType vineType = (VineTileType)msg.ReadRangedInteger(0b0000, 0b1111);
int flowerConfig = msg.ReadRangedInteger(0, 0xFFF);
int leafConfig = msg.ReadRangedInteger(0, 0xFFF);
sbyte posX = (sbyte) msg.ReadByte(), posY = (sbyte) msg.ReadByte();
sbyte posX = (sbyte)msg.ReadByte(), posY = (sbyte)msg.ReadByte();
Vector2 pos = new Vector2(posX * VineTile.Size, posY * VineTile.Size);
tiles.Add(new VineTile(this, pos, vineType, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafConfig)));

View File

@@ -69,8 +69,8 @@ namespace Barotrauma.Items.Components
var eventData = ExtractEventData<EventData>(extraData);
Vector2 attachPos = eventData.AttachPos;
msg.Write(attachPos.X);
msg.Write(attachPos.Y);
msg.WriteSingle(attachPos.X);
msg.WriteSingle(attachPos.Y);
}
public override void ClientEventRead(IReadMessage msg, float sendingTime)

View File

@@ -23,11 +23,10 @@ namespace Barotrauma.Items.Components
public void ExtractJobPrefab(IReadOnlyDictionary<Identifier, string> tags)
{
if (!tags.TryGetValue("jobid".ToIdentifier(), out string jobId)) { return; }
if (!tags.TryGetValue("jobid".ToIdentifier(), out string jobId)) { return; }
if (!jobId.IsNullOrEmpty())
{
JobPrefab = JobPrefab.Get(jobId);
JobPrefab = JobPrefab.Get(jobId.ToIdentifier());
}
}

View File

@@ -153,7 +153,7 @@ namespace Barotrauma.Items.Components
GUI.MouseOn == null && !Inventory.IsMouseOnInventory && !GameMain.Instance.Paused;
if (GUI.HideCursor)
{
crosshairSprite?.Draw(spriteBatch, crosshairPos, Color.White, 0, currentCrossHairScale);
crosshairSprite?.Draw(spriteBatch, crosshairPos, ReloadTimer <= 0.0f ? Color.White : Color.White * 0.2f, 0, currentCrossHairScale);
crosshairPointerSprite?.Draw(spriteBatch, crosshairPointerPos, 0, currentCrossHairPointerScale);
}

View File

@@ -66,6 +66,8 @@ namespace Barotrauma.Items.Components
public float IsActiveTimer;
public virtual bool RecreateGUIOnResolutionChange => false;
public GUILayoutSettings DefaultLayout { get; protected set; }
public GUILayoutSettings AlternativeLayout { get; protected set; }
@@ -148,6 +150,8 @@ namespace Barotrauma.Items.Components
public GUIFrame GuiFrame { get; set; }
public bool LockGuiFramePosition;
[Serialize(false, IsPropertySaveable.No)]
public bool AllowUIOverlap
{
@@ -365,6 +369,7 @@ namespace Barotrauma.Items.Components
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
new Vector3(position.X, position.Y, 0.0f),
0.01f,
freqMult: itemSound.RoundSound.GetRandomFrequencyMultiplier(),
muffle: SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
loopingSoundChannel.Looping = true;
//TODO: tweak
@@ -522,13 +527,11 @@ namespace Barotrauma.Items.Components
if (soundSelectionModes == null) soundSelectionModes = new Dictionary<ActionType, SoundSelectionMode>();
if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random)
{
SoundSelectionMode selectionMode = SoundSelectionMode.Random;
Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out selectionMode);
Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out SoundSelectionMode selectionMode);
soundSelectionModes[type] = selectionMode;
}
List<ItemSound> soundList = null;
if (!sounds.TryGetValue(itemSound.Type, out soundList))
if (!sounds.TryGetValue(itemSound.Type, out List<ItemSound> soundList))
{
soundList = new List<ItemSound>();
sounds.Add(itemSound.Type, soundList);
@@ -566,12 +569,86 @@ namespace Barotrauma.Items.Components
}
string style = GuiFrameSource.Attribute("style") == null ? null : GuiFrameSource.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas, Anchor.Center), style, color);
TryCreateDragHandle();
DefaultLayout = GUILayoutSettings.Load(GuiFrameSource);
if (GuiFrame != null)
{
GuiFrame.RectTransform.ParentChanged += OnGUIParentChanged;
}
GameMain.Instance.ResolutionChanged += OnResolutionChanged;
GameMain.Instance.ResolutionChanged += OnResolutionChangedPrivate;
}
protected virtual void TryCreateDragHandle()
{
if (GuiFrame != null && GuiFrameSource.GetAttributeBool("draggable", true))
{
var handle = new GUIDragHandle(new RectTransform(Vector2.One, GuiFrame.RectTransform, Anchor.Center),
GuiFrame.RectTransform, style: null)
{
DragArea = HUDLayoutSettings.ItemHUDArea
};
int iconHeight = GUIStyle.ItemFrameMargin.Y / 4;
var dragIcon = new GUIImage(new RectTransform(new Point(GuiFrame.Rect.Width, iconHeight), handle.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, iconHeight / 2) },
style: "GUIDragIndicatorHorizontal");
dragIcon.RectTransform.MinSize = new Point(0, iconHeight);
handle.ValidatePosition = (RectTransform rectT) =>
{
var activeHuds = Character.Controlled?.SelectedItem?.ActiveHUDs ?? item.ActiveHUDs;
foreach (ItemComponent ic in activeHuds)
{
if (ic == this || ic.GuiFrame == null || !ic.CanBeSelected) { continue; }
if (ic.GuiFrame.Rect.Width > GameMain.GraphicsWidth * 0.9f && ic.GuiFrame.Rect.Height > GameMain.GraphicsHeight * 0.9f)
{
//a full-screen GUIFrame (or at least close to one) - this component is doing something weird,
//an ItemContainer with no GUIFrame definition that positions itself in some other GUIFrame, some kind of an overlay?
// -> allow intersecting
continue;
}
if (dragIcon.Rect.Intersects(ic.GuiFrame.Rect))
{
GuiFrame.ImmediateFlash();
return false;
}
}
foreach (ItemComponent ic in activeHuds)
{
//refresh slots to ensure they're rendered at the correct position
(ic as ItemContainer)?.Inventory.CreateSlots();
}
return true;
};
int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f);
new GUIButton(new RectTransform(new Point(buttonHeight), handle.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
style: "GUIButtonSettings")
{
OnClicked = (btn, userdata) =>
{
GUIContextMenu.CreateContextMenu(
new ContextMenuOption("item.resetuiposition", isEnabled: true, onSelected: () =>
{
if (Character.Controlled?.SelectedItem != null && item != Character.Controlled.SelectedItem)
{
Character.Controlled.SelectedItem.ForceHUDLayoutUpdate(ignoreLocking: true);
}
else
{
item.ForceHUDLayoutUpdate(ignoreLocking: true);
}
}),
new ContextMenuOption(TextManager.Get(LockGuiFramePosition ? "item.unlockuiposition" : "item.lockuiposition"), isEnabled: true, onSelected: () =>
{
LockGuiFramePosition = !LockGuiFramePosition;
handle.Enabled = !LockGuiFramePosition;
}));
return true;
}
};
}
}
/// <summary>
@@ -631,6 +708,11 @@ namespace Barotrauma.Items.Components
CreateGUI();
}
OnResolutionChanged();
item.ForceHUDLayoutUpdate(ignoreLocking: true);
if (GuiFrame != null && GuiFrame.GetChild<GUIDragHandle>() is GUIDragHandle dragHandle)
{
dragHandle.DragArea = HUDLayoutSettings.ItemHUDArea;
}
}
public virtual void AddTooltipInfo(ref LocalizedString name, ref LocalizedString description) { }

View File

@@ -45,6 +45,8 @@ namespace Barotrauma.Items.Components
private set;
}
public override bool RecreateGUIOnResolutionChange => true;
/// <summary>
/// Depth at which the contained sprites are drawn. If not set, the original depth of the item sprites is used.
/// </summary>
@@ -189,7 +191,7 @@ namespace Barotrauma.Items.Components
onDraw: (SpriteBatch spriteBatch, GUICustomComponent component) => { Inventory.Draw(spriteBatch); },
onUpdate: null)
{
CanBeFocused = false
CanBeFocused = true
};
// Expand the frame vertically if it's too small to fit the text
@@ -322,6 +324,8 @@ namespace Barotrauma.Items.Components
int i = 0;
foreach (Item containedItem in Inventory.AllItems)
{
if (containedItem?.Sprite == null) { continue; }
if (AutoInteractWithContained)
{
containedItem.IsHighlighted = item.IsHighlighted;
@@ -342,7 +346,7 @@ namespace Barotrauma.Items.Components
containedItem.Sprite.Draw(
spriteBatch,
new Vector2(currentItemPos.X, -currentItemPos.Y),
isWiringMode ? containedItem.GetSpriteColor() * 0.15f : containedItem.GetSpriteColor(),
isWiringMode ? containedItem.GetSpriteColor(withHighlight: true) * 0.15f : containedItem.GetSpriteColor(withHighlight: true),
origin,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation ),
containedItem.Scale,
@@ -381,10 +385,15 @@ namespace Barotrauma.Items.Components
guiCustomComponent.RectTransform.Parent = Inventory.RectTransform;
}
if (item.ParentInventory?.Owner == character && character.SelectedItem == item)
{
character.SelectedItem = null;
}
//if the item is in the character's inventory, no need to update the item's inventory
//because the player can see it by hovering the cursor over the item
guiCustomComponent.Visible = item.ParentInventory?.Owner != character && DrawInventory;
if (!guiCustomComponent.Visible) { return; }
//because the player can see it by hovering the cursor over the item
guiCustomComponent.Visible = DrawInventory && item.ParentInventory?.Owner != character;
if (!guiCustomComponent.Visible) { return; }
Inventory.Update(deltaTime, cam);
}

View File

@@ -90,8 +90,8 @@ namespace Barotrauma.Items.Components
{
Light.LightSpriteEffect = Light.LightSpriteEffect == SpriteEffects.None ?
SpriteEffects.FlipHorizontally : SpriteEffects.None;
SetLightSourceTransformProjSpecific();
}
SetLightSourceTransformProjSpecific();
}
partial void OnStateChanged()

View File

@@ -1,9 +1,11 @@
using Barotrauma.Extensions;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma.Items.Components
{
@@ -15,6 +17,7 @@ namespace Barotrauma.Items.Components
}
private GUIButton activateButton;
private GUIComponent inputInventoryHolder, outputInventoryHolder;
private GUIListBox outputDisplayListBox;
private GUIComponent inSufficientPowerWarning;
@@ -31,45 +34,60 @@ namespace Barotrauma.Items.Components
[Serialize(0.0f, IsPropertySaveable.Yes)]
public float InfoAreaWidth { get; set; }
partial void InitProjSpecific(XElement element)
[Serialize(true, IsPropertySaveable.Yes)]
public bool ShowOutput { get; set; }
partial void InitProjSpecific(XElement _)
{
CreateGUI();
}
public override bool RecreateGUIOnResolutionChange => true;
protected override void OnResolutionChanged()
{
base.OnResolutionChanged();
OnItemLoadedProjSpecific();
}
protected override void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.90f, 0.80f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.88f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
Stretch = true,
Stretch = true,
RelativeSpacing = 0.08f
};
new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform), item.Name, font: GUIStyle.SubHeadingFont)
new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform) { MinSize = new Point(0, GUI.IntScale(25)) }, item.Name, font: GUIStyle.SubHeadingFont)
{
TextAlignment = Alignment.Center,
AutoScaleHorizontal = true
};
var topFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedFrame.RectTransform), style: null);
var topFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.375f), paddedFrame.RectTransform), style: null);
// === INPUT LABEL === //
var inputLabelArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.15f), topFrame.RectTransform, Anchor.TopCenter), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, inputLabelArea.RectTransform), TextManager.Get("deconstructor.input", "uilabel.input"), font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, inputLabelArea.RectTransform), style: "HorizontalLine");
var inputLabelArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.15f), topFrame.RectTransform, Anchor.TopCenter), childAnchor: Anchor.CenterLeft, isHorizontal: true);
var queueLabelLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.43f, 1f), inputLabelArea.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
var queueLabel = new GUITextBlock(new RectTransform(Vector2.One, queueLabelLayout.RectTransform), TextManager.Get("deconstructor.inputqueue"), font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
queueLabel.RectTransform.Resize(new Point((int) queueLabel.Font.MeasureString(queueLabel.Text).X, queueLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, queueLabelLayout.RectTransform), style: "HorizontalLine");
var inputLabelLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.57f, 1f), inputLabelArea.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, inputLabelLayout.RectTransform), TextManager.Get("deconstructor.input", "uilabel.input"), font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, inputLabelLayout.RectTransform), style: "HorizontalLine");
var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), topFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f };
// === INPUT SLOTS === //
inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.7f, 1f), inputArea.RectTransform), style: null);
new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawOverLay, null) { CanBeFocused = false };
@@ -78,6 +96,7 @@ namespace Barotrauma.Items.Components
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterLeft);
activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.8f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton")
{
UserData = UIHighlightAction.ElementId.DeconstructButton,
TextBlock = { AutoScaleHorizontal = true },
OnClicked = OnActivateButtonClicked
};
@@ -92,8 +111,8 @@ namespace Barotrauma.Items.Components
};
// === OUTPUT AREA === //
var bottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedFrame.RectTransform), style: null);
var bottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.375f), paddedFrame.RectTransform), style: null);
// === OUTPUT LABEL === //
var outputLabelArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.15f), bottomFrame.RectTransform, Anchor.TopCenter), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
@@ -104,10 +123,19 @@ namespace Barotrauma.Items.Components
outputLabel.RectTransform.Resize(new Point((int) outputLabel.Font.MeasureString(outputLabel.Text).X, outputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, outputLabelArea.RectTransform), style: "HorizontalLine");
var outputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), bottomFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f };
var outputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), bottomFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f };
// === OUTPUT SLOTS === //
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f - InfoAreaWidth, 1f), outputArea.RectTransform, Anchor.CenterLeft), style: null);
// === OUTPUT SLOTS === //
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f - InfoAreaWidth, 1f), outputArea.RectTransform, Anchor.CenterLeft), style: null);
if (ShowOutput)
{
GUILayoutGroup outputDisplayLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), paddedFrame.RectTransform), childAnchor: Anchor.TopCenter);
GUILayoutGroup outDisplayTopGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.2f), outputDisplayLayout.RectTransform), isHorizontal: true);
GUITextBlock outDisplayBlock = new GUITextBlock(new RectTransform(Vector2.One, outDisplayTopGroup.RectTransform), TextManager.Get("deconstructor.output"), font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
GUILayoutGroup outDisplayBottomGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.975f, 0.8f), outputDisplayLayout.RectTransform), isHorizontal: true);
outputDisplayListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f), outDisplayBottomGroup.RectTransform), isHorizontal: true, style: null);
}
if (InfoAreaWidth >= 0.0f)
{
@@ -181,7 +209,7 @@ namespace Barotrauma.Items.Components
{
if (!(linkedTo is Item { DisplaySideBySideWhenLinked: true } linkedItem)) { continue; }
if (!linkedItem.Components.Any()) { continue; }
var itemContainer = linkedItem.GetComponent<ItemContainer>();
if (itemContainer?.GuiFrame == null || itemContainer.AllowUIOverlap) { continue; }
@@ -195,12 +223,178 @@ namespace Barotrauma.Items.Components
return base.Select(character);
}
partial void OnItemSlotsChanged(ItemContainer container)
{
if (container.Inventory is null) { return; }
RefreshOutputDisplay(container.Inventory.AllItems.ToImmutableArray());
}
private void RefreshOutputDisplay(ImmutableArray<Item> items)
{
const string outputItemCountUserData = "OutputItemCount";
const string questionMarkUserData = "UnknownItemOutput";
if (outputDisplayListBox is null || inputContainer.Inventory is null) { return; }
Dictionary<Identifier, int> itemCounts = new Dictionary<Identifier, int>();
Dictionary<Identifier, GUIComponent> children = new Dictionary<Identifier, GUIComponent>();
bool addQuestionMark = false;
foreach (GUIComponent child in outputDisplayListBox.Content.Children)
{
if (child.UserData is Identifier it)
{
children.Add(it, child);
}
}
if (outputDisplayListBox.Content.FindChild(questionMarkUserData) is { } foundChild)
{
outputDisplayListBox.RemoveChild(foundChild);
}
foreach (Item it in items)
{
if (it.Prefab.RandomDeconstructionOutput)
{
addQuestionMark = true;
continue;
}
foreach (DeconstructItem deconstructItem in it.Prefab.DeconstructItems)
{
if (!deconstructItem.IsValidDeconstructor(item)) { continue; }
RegisterItem(deconstructItem.ItemIdentifier, deconstructItem.Amount);
}
/*if (it.OwnInventory is { } inventory)
{
foreach (Item inventoryItems in inventory.AllItems)
{
RegisterItem(inventoryItems.Prefab.Identifier);
}
}*/
void RegisterItem(Identifier identifier, int amount = 1)
{
if (itemCounts.ContainsKey(identifier))
{
itemCounts[identifier] += amount;
return;
}
itemCounts.Add(identifier, amount);
}
}
foreach (var (it, child) in children)
{
if (!itemCounts.ContainsKey(it))
{
outputDisplayListBox.RemoveChild(child);
}
}
foreach (var (it, amount) in itemCounts)
{
if (!children.TryGetValue(it, out GUIComponent child))
{
child = CreateOutputDisplayItem(it, outputDisplayListBox.Content);
}
if (child is null) { continue; }
UpdateOutputDisplayItemCount(child, amount);
}
if (addQuestionMark)
{
CreateQuestionMark(outputDisplayListBox.Content);
}
static void CreateQuestionMark(GUIComponent parent)
{
GUIFrame itemFrame = new GUIFrame(new RectTransform(new Vector2(0.1f, 1f), parent.RectTransform), style: null)
{
UserData = questionMarkUserData,
ToolTip = TextManager.Get("deconstructor.unknownitemsoutput")
};
GUIFrame questionMarkFrame = new GUIFrame(new RectTransform(Vector2.One, itemFrame.RectTransform, scaleBasis: ScaleBasis.Smallest, anchor: Anchor.Center), style: "GUIFrameListBox")
{
CanBeFocused = false,
};
// question mark text
new GUITextBlock(new RectTransform(Vector2.One, questionMarkFrame.RectTransform, anchor: Anchor.Center), text: "?", textAlignment: Alignment.Center, font: GUIStyle.LargeFont)
{
CanBeFocused = false
};
}
static GUIComponent CreateOutputDisplayItem(Identifier identifier, GUIComponent parent)
{
ItemPrefab prefab = ItemPrefab.Find(null, identifier);
if (prefab is null) { return null; }
GUIFrame itemFrame = new GUIFrame(new RectTransform(new Vector2(0.1f, 1f), parent.RectTransform), style: null)
{
UserData = identifier,
ToolTip = GetTooltip(prefab)
};
Sprite icon = prefab.InventoryIcon ?? prefab.Sprite;
Color iconColor = prefab.InventoryIcon is null ? prefab.SpriteColor : prefab.InventoryIconColor;
GUIImage itemIcon = new GUIImage(new RectTransform(Vector2.One, itemFrame.RectTransform, scaleBasis: ScaleBasis.Smallest, anchor: Anchor.Center), sprite: icon, scaleToFit: true)
{
Color = iconColor,
CanBeFocused = false
};
// item count text
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), itemIcon.RectTransform, anchor: Anchor.BottomRight), "", font: GUIStyle.Font, textAlignment: Alignment.BottomRight)
{
UserData = outputItemCountUserData,
Shadow = true,
CanBeFocused = false,
Padding = Vector4.Zero,
TextColor = Color.White,
};
return itemFrame;
}
static void UpdateOutputDisplayItemCount(GUIComponent component, int count)
{
if (!(component.FindChild(outputItemCountUserData, recursive: true) is GUITextBlock textBlock)) { return; }
textBlock.Text = TextManager.GetWithVariable("campaignstore.quantity", "[amount]", count.ToString());
}
static RichString GetTooltip(ItemPrefab prefab)
{
LocalizedString toolTip = $"‖color:{Color.White.ToStringHex()}‖{prefab.Name}‖color:end‖";
LocalizedString description = prefab.Description;
if (!description.IsNullOrEmpty()) { toolTip += '\n' + description; }
if (prefab.ContentPackage != GameMain.VanillaContent && prefab.ContentPackage != null)
{
toolTip += $"\n‖color:{Color.MediumPurple.ToStringHex()}‖{prefab.ContentPackage.Name}‖color:end‖";
}
return RichString.Rich(toolTip);
}
}
partial void OnItemLoadedProjSpecific()
{
inputContainer.AllowUIOverlap = true;
inputContainer.Inventory.RectTransform = inputInventoryHolder.RectTransform;
outputContainer.AllowUIOverlap = true;
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
inputContainer.Inventory.Locked = IsActive;
}
private void DrawOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
@@ -208,7 +402,7 @@ namespace Barotrauma.Items.Components
overlayComponent.RectTransform.SetAsLastChild();
if (!(inputContainer?.Inventory?.visualSlots is { } visualSlots)) { return; }
if (DeconstructItemsSimultaneously)
{
for (int i = 0; i < InputContainer.Inventory.Capacity; i++)
@@ -239,17 +433,22 @@ namespace Barotrauma.Items.Components
private bool OnActivateButtonClicked(GUIButton button, object obj)
{
var disallowedItem = inputContainer.Inventory.FindItem(i => !i.AllowDeconstruct, recursive: false);
if (disallowedItem != null && !DeconstructItemsSimultaneously)
if (!IsActive)
{
int index = inputContainer.Inventory.FindIndex(disallowedItem);
if (index >= 0 && index < inputContainer.Inventory.visualSlots.Length)
//don't allow turning on if there's non-deconstructable items in the queue
var disallowedItem = inputContainer.Inventory.FindItem(i => !i.AllowDeconstruct, recursive: false);
if (disallowedItem != null && !DeconstructItemsSimultaneously)
{
var slot = inputContainer.Inventory.visualSlots[index];
slot?.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f);
int index = inputContainer.Inventory.FindIndex(disallowedItem);
if (index >= 0 && index < inputContainer.Inventory.visualSlots.Length)
{
var slot = inputContainer.Inventory.visualSlots[index];
slot?.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f);
}
return true;
}
return true;
}
if (GameMain.Client != null)
{
pendingState = !IsActive;
@@ -264,7 +463,7 @@ namespace Barotrauma.Items.Components
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
msg.Write(pendingState);
msg.WriteBoolean(pendingState);
}
public void ClientEventRead(IReadMessage msg, float sendingTime)

View File

@@ -32,6 +32,8 @@ namespace Barotrauma.Items.Components
}
private FabricationRecipe selectedItem;
public Identifier SelectedItemIdentifier => SelectedItem?.TargetItem.Identifier ?? Identifier.Empty;
private GUIComponent inSufficientPowerWarning;
private FabricationRecipe pendingFabricatedItem;
@@ -51,11 +53,13 @@ namespace Barotrauma.Items.Components
[Serialize("vendingmachine.outofstock", IsPropertySaveable.Yes)]
public string FabricationLimitReachedText { get; set; }
public override bool RecreateGUIOnResolutionChange => true;
protected override void OnResolutionChanged()
{
if (GuiFrame != null)
{
OnItemLoadedProjSpecific();
InitInventoryUIs();
}
}
@@ -232,6 +236,17 @@ namespace Barotrauma.Items.Components
}
}
private void InitInventoryUIs()
{
if (inputInventoryHolder != null)
{
inputContainer.AllowUIOverlap = true;
inputContainer.Inventory.RectTransform = inputInventoryHolder.RectTransform;
}
outputContainer.AllowUIOverlap = true;
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
}
private LocalizedString GetRecipeNameAndAmount(FabricationRecipe fabricationRecipe)
{
if (fabricationRecipe == null) { return ""; }
@@ -249,13 +264,7 @@ namespace Barotrauma.Items.Components
partial void OnItemLoadedProjSpecific()
{
CreateGUI();
if (inputInventoryHolder != null)
{
inputContainer.AllowUIOverlap = true;
inputContainer.Inventory.RectTransform = inputInventoryHolder.RectTransform;
}
outputContainer.AllowUIOverlap = true;
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
InitInventoryUIs();
}
partial void SelectProjSpecific(Character character)
@@ -328,76 +337,112 @@ namespace Barotrauma.Items.Components
}
}
private readonly Dictionary<FabricationRecipe.RequiredItem, int> missingIngredientCounts = new Dictionary<FabricationRecipe.RequiredItem, int>();
private float ingredientHighlightTimer;
private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{
overlayComponent.RectTransform.SetAsLastChild();
missingIngredientCounts.Clear();
FabricationRecipe targetItem = fabricatedItem ?? selectedItem;
if (targetItem != null)
{
int slotIndex = 0;
var missingItems = new List<FabricationRecipe.RequiredItem>();
foreach (FabricationRecipe.RequiredItem requiredItem in targetItem.RequiredItems)
{
for (int i = 0; i < requiredItem.Amount; i++)
if (missingIngredientCounts.ContainsKey(requiredItem))
{
missingItems.Add(requiredItem);
missingIngredientCounts[requiredItem] += requiredItem.Amount;
}
else
{
missingIngredientCounts[requiredItem] = requiredItem.Amount;
}
}
foreach (Item item in inputContainer.Inventory.AllItems)
{
missingItems.Remove(missingItems.FirstOrDefault(mi => mi.ItemPrefabs.Contains(item.Prefab)));
}
var missingCounts = missingItems.GroupBy(missingItem => missingItem).ToDictionary(x => x.Key, x => x.Count());
missingItems = missingItems.Distinct().ToList();
var missingIngredient = missingIngredientCounts.Keys.FirstOrDefault(mi => mi.MatchesItem(item));
if (missingIngredient == null) { continue; }
foreach (FabricationRecipe.RequiredItem requiredItem in missingItems)
if (missingIngredientCounts[missingIngredient] == 1)
{
missingIngredientCounts.Remove(missingIngredient);
}
else
{
missingIngredientCounts[missingIngredient]--;
}
}
if (ingredientHighlightTimer <= 0.0f)
{
//highlight inventory slots that contain suitable ingredients in linked inventories
foreach (var inventory in linkedInventories)
{
if (inventory.visualSlots == null) { continue; }
for (int i = 0; i < inventory.Capacity; i++)
{
if (inventory.visualSlots[i].HighlightTimer > 0.0f) { continue; }
var availableItem = inventory.GetItemAt(i);
if (availableItem == null) { continue; }
if (missingIngredientCounts.Keys.Any(it => it.MatchesItem(availableItem)))
{
inventory.visualSlots[i].ShowBorderHighlight(GUIStyle.Green, 0.5f, 0.5f, 0.2f);
continue;
}
if (availableItem.OwnInventory != null)
{
for (int j = 0; j < availableItem.OwnInventory.Capacity; j++)
{
var availableContainedItem = availableItem.OwnInventory.GetItemAt(i);
if (availableContainedItem == null) { continue; }
if (missingIngredientCounts.Keys.Any(it => it.MatchesItem(availableContainedItem)))
{
inventory.visualSlots[i].ShowBorderHighlight(GUIStyle.Green, 0.5f, 0.5f, 0.2f);
break;
}
}
}
}
}
ingredientHighlightTimer = 1.0f;
}
int slotIndex = 0;
foreach (var kvp in missingIngredientCounts)
{
var requiredItem = kvp.Key;
int missingCount = kvp.Value;
while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.GetItemAt(slotIndex) != null)
{
slotIndex++;
}
requiredItem.ItemPrefabs
.Where(requiredPrefab => availableIngredients.ContainsKey(requiredPrefab.Identifier))
.ForEach(requiredPrefab => {
var availableItems = availableIngredients[requiredPrefab.Identifier];
foreach (Item it in availableItems)
{
if (it.ParentInventory == inputContainer.Inventory) { continue; }
var rootInventoryOwner = it.GetRootInventoryOwner();
Inventory rootInventory = (rootInventoryOwner as Item)?.OwnInventory as Inventory ?? (rootInventoryOwner as Character)?.Inventory;
if (rootInventory?.visualSlots == null) { continue; }
int availableSlotIndex = rootInventory.FindIndex((it.Container != rootInventoryOwner ? it.Container : it) ?? it);
if (availableSlotIndex < 0) { continue; }
if (rootInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{
rootInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUIStyle.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity)
{
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUIStyle.Green, 0.5f, 0.5f, 0.2f);
}
}
}
});
if (slotIndex >= inputContainer.Capacity) { break; }
var itemIcon = requiredItem.ItemPrefabs.First().InventoryIcon ?? requiredItem.ItemPrefabs.First().Sprite;
if (slotIndex < inputContainer.Capacity &&
inputContainer.Inventory.visualSlots[slotIndex].HighlightTimer <= 0.0f &&
availableIngredients.Any(i => i.Value.Any() && requiredItem.MatchesItem(i.Value.First())))
{
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUIStyle.Green, 0.5f, 0.5f, 0.2f);
}
var requiredItemPrefab = requiredItem.FirstMatchingPrefab;
var itemIcon = requiredItemPrefab.InventoryIcon ?? requiredItemPrefab.Sprite;
Rectangle slotRect = inputContainer.Inventory.visualSlots[slotIndex].Rect;
itemIcon.Draw(
spriteBatch,
slotRect.Center.ToVector2(),
color: requiredItem.ItemPrefabs.First().InventoryIconColor * 0.3f,
color: requiredItemPrefab.InventoryIconColor * 0.3f,
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
if (missingCounts[requiredItem] > 1)
if (missingCount > 1)
{
Vector2 stackCountPos = new Vector2(slotRect.Right, slotRect.Bottom);
string stackCountText = "x" + missingCounts[requiredItem];
string stackCountText = "x" + missingCount;
stackCountPos -= GUIStyle.SmallFont.MeasureString(stackCountText) + new Vector2(4, 2);
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos + Vector2.One, Color.Black);
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos, Color.White);
@@ -446,9 +491,9 @@ namespace Barotrauma.Items.Components
{
toolTipText = TextManager.GetWithVariable("displayname.emptyitem", "[itemname]", toolTipText);
}
if (!requiredItem.ItemPrefabs.First().Description.IsNullOrEmpty())
if (!requiredItemPrefab.Description.IsNullOrEmpty())
{
toolTipText += '\n' + requiredItem.ItemPrefabs.First().Description;
toolTipText += '\n' + requiredItemPrefab.Description;
}
tooltip = new ToolTip { TargetElement = slotRect, Tooltip = toolTipText };
}
@@ -715,6 +760,8 @@ namespace Barotrauma.Items.Components
activateButton.Enabled = false;
inSufficientPowerWarning.Visible = IsActive && !hasPower;
ingredientHighlightTimer -= deltaTime;
if (!IsActive)
{
//only check ingredients if the fabricator isn't active (if it is, this is done in Update)
@@ -763,7 +810,7 @@ namespace Barotrauma.Items.Components
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
uint recipeHash = pendingFabricatedItem?.RecipeHash ?? 0;
msg.Write(recipeHash);
msg.WriteUInt32(recipeHash);
}
public void ClientEventRead(IReadMessage msg, float sendingTime)

View File

@@ -246,6 +246,7 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI()
{
GuiFrame.ClearChildren();
TryCreateDragHandle();
GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f);
GuiFrame.CanBeFocused = true;
@@ -509,7 +510,7 @@ namespace Barotrauma.Items.Components
Vector2 origin = weaponSprite.Origin;
float scale = parentWidth / Math.Max(weaponSprite.size.X, weaponSprite.size.Y);
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? Color.DimGray : GUIStyle.Green;
weaponSprite.Draw(batch, center, color, origin, rotation, scale, it.SpriteEffects);
weaponSprite.Draw(batch, center, color, origin, rotation, scale, SpriteEffects.None);
}
});
@@ -1012,12 +1013,14 @@ namespace Barotrauma.Items.Components
}
else if (hullData.LinkedHulls.Any())
{
hullData.HullWaterAmount = 0.0f;
float waterVolume = 0.0f;
float totalVolume = 0.0f;
foreach (Hull linkedHull in hullData.LinkedHulls)
{
hullData.HullWaterAmount += WaterDetector.GetWaterPercentage(linkedHull);
waterVolume += linkedHull.WaterVolume;
totalVolume += linkedHull.Volume;
}
hullData.HullWaterAmount /= hullData.LinkedHulls.Count;
hullData.HullWaterAmount = MathHelper.Clamp((int)Math.Ceiling(waterVolume / totalVolume * 100), 0, 100);
}
else
{

View File

@@ -25,7 +25,7 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (Character.Controlled?.SelectedConstruction != item)
if (Character.Controlled?.SelectedItem != item)
{
IsActive = false;
return;

View File

@@ -1,10 +1,8 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -58,6 +56,7 @@ namespace Barotrauma.Items.Components
RelativeOffset = new Vector2(0, 0.1f)
}, style: "PowerButton")
{
UserData = UIHighlightAction.ElementId.PowerButton,
OnClicked = (button, data) =>
{
TargetLevel = null;
@@ -114,6 +113,7 @@ namespace Barotrauma.Items.Components
return true;
}
};
pumpSpeedSlider.Frame.UserData = UIHighlightAction.ElementId.PumpSpeedSlider;
var textsArea = new GUIFrame(new RectTransform(new Vector2(1, 0.25f), sliderArea.RectTransform, Anchor.BottomCenter), style: null);
var outLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), textsArea.RectTransform, Anchor.CenterLeft), TextManager.Get("PumpOut"),
textColor: GUIStyle.TextColorNormal, textAlignment: Alignment.CenterLeft, wrap: false, font: GUIStyle.SubHeadingFont);
@@ -219,7 +219,7 @@ namespace Barotrauma.Items.Components
{
//flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this
msg.WriteRangedInteger((int)(flowPercentage / 10.0f), -10, 10);
msg.Write(IsActive);
msg.WriteBoolean(IsActive);
}
public void ClientEventRead(IReadMessage msg, float sendingTime)

View File

@@ -1,11 +1,11 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Barotrauma.Sounds;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -34,12 +34,14 @@ namespace Barotrauma.Items.Components
private Color optimalRangeColor = new Color(74,238,104,255);
private Color offRangeColor = Color.Orange;
private Color warningColor = Color.Red;
private Color coldColor = Color.LightBlue;
private Color warmColor = Color.Orange;
private Color hotColor = Color.Red;
private readonly Color[] temperatureColors = new Color[] { Color.Blue, Color.LightBlue, Color.Orange, Color.Red };
private Color outputColor = Color.Goldenrod;
private Color loadColor = Color.LightSteelBlue;
private RoundSound temperatureBoostSoundUp, temperatureBoostSoundDown;
private GUIButton temperatureBoostUpButton, temperatureBoostDownButton;
public GUIScrollBar FissionRateScrollBar { get; private set; }
public GUIScrollBar TurbineOutputScrollBar { get; private set; }
@@ -63,10 +65,11 @@ namespace Barotrauma.Items.Components
"ReactorWarningOverheating", "ReactorWarningHighOutput", "ReactorWarningFuelOut", "ReactorWarningSCRAM"
};
public override bool RecreateGUIOnResolutionChange => true;
partial void InitProjSpecific(ContentXElement element)
{
// TODO: need to recreate the gui when the resolution changes
CreateGUI();
fissionRateMeter = new Sprite(element.GetChildElement("fissionratemeter")?.GetChildElement("sprite"));
turbineOutputMeter = new Sprite(element.GetChildElement("turbineoutputmeter")?.GetChildElement("sprite"));
meterPointer = new Sprite(element.GetChildElement("meterpointer")?.GetChildElement("sprite"));
@@ -76,10 +79,28 @@ namespace Barotrauma.Items.Components
tempRangeIndicator = new Sprite(element.GetChildElement("temprangeindicator")?.GetChildElement("sprite"));
graphLine = new Sprite(element.GetChildElement("graphline")?.GetChildElement("sprite"));
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "temperatureboostsoundup":
temperatureBoostSoundUp = RoundSound.Load(subElement, false);
break;
case "temperatureboostsounddown":
temperatureBoostSoundDown = RoundSound.Load(subElement, false);
break;
}
}
}
protected override void CreateGUI()
{
warningButtons.Clear();
paddedFrame = new GUILayoutGroup(new RectTransform(
GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center)
{ AbsoluteOffset = GUIStyle.ItemFrameOffset },
isHorizontal: true)
GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center)
{ AbsoluteOffset = GUIStyle.ItemFrameOffset },
isHorizontal: true)
{
RelativeSpacing = 0.012f,
Stretch = true
@@ -213,6 +234,7 @@ namespace Barotrauma.Items.Components
return false;
}
};
FissionRateScrollBar.Frame.UserData = UIHighlightAction.ElementId.FissionRateSlider;
TurbineOutputScrollBar = new GUIScrollBar(new RectTransform(sliderSize, rightArea.RectTransform, Anchor.TopCenter)
{
@@ -231,6 +253,7 @@ namespace Barotrauma.Items.Components
return false;
}
};
TurbineOutputScrollBar.Frame.UserData = UIHighlightAction.ElementId.TurbineOutputSlider;
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.2f), columnLeft.RectTransform))
{
@@ -281,9 +304,10 @@ namespace Barotrauma.Items.Components
new GUIFrame(new RectTransform(new Vector2(0.01f, 1.0f), topRightArea.RectTransform), style: "VerticalLine");
AutoTempSwitch = new GUIButton(new RectTransform(new Vector2(0.15f, 0.9f), topRightArea.RectTransform),
AutoTempSwitch = new GUIButton(new RectTransform(new Vector2(0.15f, 0.9f), topRightArea.RectTransform),
style: "SwitchVertical")
{
UserData = UIHighlightAction.ElementId.AutoTempSwitch,
Enabled = false,
Selected = AutoTemp,
ClickSound = GUISoundType.UISwitch,
@@ -326,6 +350,7 @@ namespace Barotrauma.Items.Components
RelativeOffset = new Vector2(0, 0.1f)
}, style: "PowerButton")
{
UserData = UIHighlightAction.ElementId.PowerButton,
OnClicked = (button, data) =>
{
PowerOn = !PowerOn;
@@ -354,7 +379,46 @@ namespace Barotrauma.Items.Components
new GUIFrame(new RectTransform(new Vector2(0.01f, 1.0f), bottomRightArea.RectTransform), style: "VerticalLine");
new GUICustomComponent(new RectTransform(new Vector2(0.1f, 1), bottomRightArea.RectTransform, Anchor.Center), DrawTempMeter, null);
var temperatureArea = new GUILayoutGroup(new RectTransform(new Vector2(0.1f, 1), bottomRightArea.RectTransform, Anchor.Center), isHorizontal: false)
{
Stretch = true,
RelativeSpacing = 0.01f
};
temperatureBoostUpButton = new GUIButton(new RectTransform(Vector2.One, temperatureArea.RectTransform, scaleBasis: ScaleBasis.BothWidth), style: "GUIPlusButton")
{
ToolTip = TextManager.Get("reactor.temperatureboostup"),
OnClicked = (_, __) =>
{
applyTemperatureBoost(TemperatureBoostAmount, temperatureBoostSoundUp);
return true;
}
};
new GUICustomComponent(new RectTransform(Vector2.One, temperatureArea.RectTransform, Anchor.Center), DrawTempMeter, null);
temperatureBoostDownButton = new GUIButton(new RectTransform(Vector2.One, temperatureArea.RectTransform, scaleBasis: ScaleBasis.BothWidth), style: "GUIMinusButton")
{
ToolTip = TextManager.Get("reactor.temperatureboostdown"),
OnClicked = (_, __) =>
{
applyTemperatureBoost(-TemperatureBoostAmount, temperatureBoostSoundDown);
return true;
}
};
void applyTemperatureBoost(float amount, RoundSound sound)
{
temperatureBoost = amount;
if (sound != null)
{
SoundPlayer.PlaySound(
sound.Sound,
item.WorldPosition,
sound.Volume,
sound.Range,
hullGuess: item.CurrentHull);
}
}
var graphArea = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), bottomRightArea.RectTransform))
{
@@ -382,6 +446,19 @@ namespace Barotrauma.Items.Components
};
LocalizedString outputStr = TextManager.Get("ReactorOutput");
outputText.TextGetter += () => $"{outputStr.Replace("[kw]", ((int)-currPowerConsumption).ToString())} {kW}";
InitInventoryUI();
}
private void InitInventoryUI()
{
var itemContainer = item.GetComponent<ItemContainer>();
if (itemContainer != null)
{
itemContainer.UILabel = "";
itemContainer.AllowUIOverlap = true;
itemContainer.Inventory.RectTransform = inventoryContainer.RectTransform;
}
}
public override void OnItemLoaded()
@@ -389,23 +466,10 @@ namespace Barotrauma.Items.Components
base.OnItemLoaded();
TurbineOutputScrollBar.BarScroll = TargetTurbineOutput / 100.0f;
FissionRateScrollBar.BarScroll = TargetFissionRate / 100.0f;
var itemContainer = item.GetComponent<ItemContainer>();
if (itemContainer != null)
{
itemContainer.UILabel = "";
itemContainer.AllowUIOverlap = true;
itemContainer.Inventory.RectTransform = inventoryContainer.RectTransform;
/*var inventoryLabel = inventoryContainer.Parent?.GetChild<GUITextBlock>();
if (inventoryLabel != null)
{
inventoryLabel.RectTransform.MinSize = new Point(100, 0);
inventoryLabel.Text = itemContainer.GetUILabel();
inventoryLabel.CalculateHeightFromText();
(inventoryLabel.Parent as GUILayoutGroup).Recalculate();
}*/
}
InitInventoryUI();
}
private void DrawTempMeter(SpriteBatch spriteBatch, GUICustomComponent container)
{
Vector2 meterPos = new Vector2(container.Rect.X, container.Rect.Y);
@@ -418,7 +482,7 @@ namespace Barotrauma.Items.Components
while (meterBarPos.Y > container.Rect.Bottom + (int)(5 * GUI.yScale) - container.Rect.Height * tempFill)
{
float tempRatio = 1.0f - ((meterBarPos.Y - container.Rect.Y) / container.Rect.Height);
Color color = ToolBox.GradientLerp(tempRatio, coldColor, optimalRangeColor, warmColor, hotColor);
Color color = ToolBox.GradientLerp(tempRatio, temperatureColors);
tempMeterBar.Draw(spriteBatch, meterBarPos, color: color, scale: meterBarScale);
int spacing = 2;
meterBarPos.Y -= tempMeterBar.size.Y * meterBarScale + spacing;
@@ -635,7 +699,6 @@ namespace Barotrauma.Items.Components
float normalizedValue = (value - range.X) / (range.Y - range.X);
float valueRad = MathHelper.Lerp(sectorRad.X, sectorRad.Y, normalizedValue);
Vector2 offset = new Vector2(0, 40) * scale;
meterPointer.Draw(spriteBatch, pointerPos, valueRad, scale);
}
@@ -713,8 +776,8 @@ namespace Barotrauma.Items.Components
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
msg.Write(autoTemp);
msg.Write(PowerOn);
msg.WriteBoolean(autoTemp);
msg.WriteBoolean(PowerOn);
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);

View File

@@ -140,8 +140,7 @@ namespace Barotrauma.Items.Components
set;
}
private bool AllowUsingMineralScanner =>
HasMineralScanner && !isConnectedToSteering;
public override bool RecreateGUIOnResolutionChange => true;
partial void InitProjSpecific(ContentXElement element)
{
@@ -195,7 +194,6 @@ namespace Barotrauma.Items.Components
protected override void OnResolutionChanged()
{
base.OnResolutionChanged();
UpdateGUIElements();
}
@@ -218,6 +216,7 @@ namespace Barotrauma.Items.Components
var sonarModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.TopCenter), style: null);
SonarModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), sonarModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{
UserData = UIHighlightAction.ElementId.SonarModeSwitch,
Selected = false,
Enabled = true,
ClickSound = GUISoundType.UISwitch,
@@ -240,6 +239,7 @@ namespace Barotrauma.Items.Components
passiveTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), sonarModeRightSide.RectTransform, Anchor.TopLeft),
TextManager.Get("SonarPassive"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall")
{
UserData = UIHighlightAction.ElementId.PassiveSonarIndicator,
ToolTip = TextManager.Get("SonarTipPassive"),
Selected = true,
Enabled = false
@@ -247,6 +247,7 @@ namespace Barotrauma.Items.Components
activeTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), sonarModeRightSide.RectTransform, Anchor.BottomLeft),
TextManager.Get("SonarActive"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRedSmall")
{
UserData = UIHighlightAction.ElementId.ActiveSonarIndicator,
ToolTip = TextManager.Get("SonarTipActive"),
Selected = false,
Enabled = false
@@ -281,9 +282,14 @@ namespace Barotrauma.Items.Components
};
new GUIFrame(new RectTransform(new Vector2(0.8f, 0.01f), paddedControlContainer.RectTransform, Anchor.Center), style: "HorizontalLine")
{ UserData = "horizontalline" };
{
UserData = "horizontalline"
};
var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null);
var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null)
{
UserData = UIHighlightAction.ElementId.DirectionalSonarFrame
};
directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
{
OnClicked = (button, data) =>
@@ -302,7 +308,7 @@ namespace Barotrauma.Items.Components
TextManager.Get("SonarDirectionalPing"), GUIStyle.TextColorNormal, GUIStyle.SubHeadingFont, Alignment.CenterLeft);
textBlocksToScaleAndNormalize.Add(directionalModeSwitchText);
if (AllowUsingMineralScanner)
if (HasMineralScanner)
{
AddMineralScannerSwitchToGUI();
}
@@ -336,6 +342,18 @@ namespace Barotrauma.Items.Components
sonarView.RectTransform.RelativeOffset = new Vector2(0.13f * GUI.RelativeHorizontalAspectRatio, 0);
sonarView.RectTransform.SetPosition(Anchor.BottomRight);
}
var handle = GuiFrame.GetChild<GUIDragHandle>();
if (handle != null)
{
handle.RectTransform.Parent = controlContainer.RectTransform;
handle.RectTransform.Resize(Vector2.One);
handle.RectTransform.SetAsFirstChild();
}
}
protected override void TryCreateDragHandle()
{
base.TryCreateDragHandle();
}
private void SetPingDirection(Vector2 direction)
@@ -373,7 +391,7 @@ namespace Barotrauma.Items.Components
{
base.OnItemLoaded();
zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom);
if (AllowUsingMineralScanner && mineralScannerSwitch == null)
if (HasMineralScanner && mineralScannerSwitch == null)
{
AddMineralScannerSwitchToGUI();
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
@@ -417,12 +435,32 @@ namespace Barotrauma.Items.Components
unsentChanges = true;
correctionTimer = CorrectionDelay;
}
return true;
}
};
var mineralScannerSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), mineralScannerFrame.RectTransform, Anchor.CenterRight),
TextManager.Get("SonarMineralScanner"), GUIStyle.TextColorNormal, GUIStyle.SubHeadingFont, Alignment.CenterLeft);
textBlocksToScaleAndNormalize.Add(mineralScannerSwitchText);
PreventMineralScannerOverlap();
}
private void PreventMineralScannerOverlap()
{
if (item.GetComponent<Steering>() is { } steering && controlContainer is { } container)
{
int containerBottom = container.Rect.Y + container.Rect.Height,
steeringTop = steering.ControlContainer.Rect.Top;
int amountRaised = 0;
while (GetContainerBottom() > steeringTop) { amountRaised++; }
container.RectTransform.AbsoluteOffset = new Point(0, -amountRaised);
int GetContainerBottom() => containerBottom - amountRaised;
}
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
@@ -502,7 +540,7 @@ namespace Barotrauma.Items.Components
Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) <
(sonarView.Rect.Width / 2 * sonarView.Rect.Width / 2);
if (AllowUsingMineralScanner && Level.Loaded != null && !Level.Loaded.Generating)
if (HasMineralScanner && Level.Loaded != null && !Level.Loaded.Generating)
{
if (MineralClusters == null)
{
@@ -985,7 +1023,7 @@ namespace Barotrauma.Items.Components
missionIndex++;
}
if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null &&
if (HasMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null &&
(item.CurrentHull == null || !DetectSubmarineWalls))
{
foreach (var c in MineralClusters)
@@ -995,6 +1033,19 @@ namespace Barotrauma.Items.Components
if (!CheckResourceMarkerVisibility(c.center, transducerCenter)) { continue; }
var i = unobtainedMinerals.FirstOrDefault();
if (i == null) { continue; }
bool disrupted = false;
foreach ((Vector2 disruptPos, float disruptStrength) in disruptedDirections)
{
float dot = Vector2.Dot(Vector2.Normalize(c.center - transducerCenter), disruptPos);
if (dot > 1.0f - disruptStrength)
{
disrupted = true;
break;
}
}
if (disrupted) { continue; }
DrawMarker(spriteBatch,
i.Name, "mineral".ToIdentifier(), "mineralcluster" + i,
c.center, transducerCenter,
@@ -1754,17 +1805,17 @@ namespace Barotrauma.Items.Components
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
msg.Write(currentMode == Mode.Active);
msg.WriteBoolean(currentMode == Mode.Active);
if (currentMode == Mode.Active)
{
msg.WriteRangedSingle(zoom, MinZoom, MaxZoom, 8);
msg.Write(useDirectionalPing);
msg.WriteBoolean(useDirectionalPing);
if (useDirectionalPing)
{
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
}
msg.Write(useMineralScanner);
msg.WriteBoolean(useMineralScanner);
}
}

View File

@@ -1,13 +1,11 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.Extensions;
using System.Linq;
namespace Barotrauma.Items.Components
{
@@ -25,7 +23,9 @@ namespace Barotrauma.Items.Components
private GUITickBox maintainPosTickBox, levelEndTickBox, levelStartTickBox;
private GUIComponent statusContainer, dockingContainer, controlContainer;
private GUIComponent statusContainer, dockingContainer;
public GUIComponent ControlContainer { get; private set; }
private bool dockingNetworkMessagePending;
@@ -90,6 +90,24 @@ namespace Barotrauma.Items.Components
}
}
private bool disableControls;
/// <summary>
/// Can be used by status effects to disable all the UI controls
/// </summary>
public bool DisableControls
{
get { return disableControls; }
set
{
if (disableControls == value) { return; }
disableControls = value;
UpdateGUIElements();
}
}
public override bool RecreateGUIOnResolutionChange => true;
partial void InitProjSpecific(ContentXElement element)
{
foreach (var subElement in element.Elements())
@@ -112,8 +130,8 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI()
{
controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterRight), "ItemUI");
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
ControlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterRight), "ItemUI");
var paddedControlContainer = new GUIFrame(new RectTransform(ControlContainer.Rect.Size - GUIStyle.ItemFrameMargin, ControlContainer.RectTransform, Anchor.Center)
{
AbsoluteOffset = GUIStyle.ItemFrameOffset
}, style: null);
@@ -121,6 +139,7 @@ namespace Barotrauma.Items.Components
var steeringModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), paddedControlContainer.RectTransform, Anchor.TopLeft), style: null);
steeringModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), steeringModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{
UserData = UIHighlightAction.ElementId.SteeringModeSwitch,
Selected = autoPilot,
Enabled = true,
ClickSound = GUISoundType.UISwitch,
@@ -162,6 +181,7 @@ namespace Barotrauma.Items.Components
maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.TopCenter),
TextManager.Get("SteeringMaintainPos"), font: GUIStyle.SmallFont, style: "GUIRadioButton")
{
UserData = UIHighlightAction.ElementId.MaintainPosTickBox,
Enabled = autoPilot,
Selected = maintainPos,
OnSelected = tickBox =>
@@ -472,7 +492,6 @@ namespace Barotrauma.Items.Components
protected override void OnResolutionChanged()
{
base.OnResolutionChanged();
UpdateGUIElements();
}
@@ -658,6 +677,11 @@ namespace Barotrauma.Items.Components
}
}
if (DisableControls)
{
dockingModeEnabled = false;
}
dockingContainer.Visible = DockingModeEnabled;
statusContainer.Visible = !DockingModeEnabled;
if (!DockingModeEnabled)
@@ -745,7 +769,7 @@ namespace Barotrauma.Items.Components
iceSpireWarningText.Visible = item.Submarine != null && !pressureWarningText.Visible && showIceSpireWarning && Timing.TotalTime % 1.0f < 0.8f;
if (Vector2.DistanceSquared(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerRadius * steerRadius)
if (!disableControls && Vector2.DistanceSquared(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerRadius * steerRadius)
{
if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen && !GameSession.IsTabMenuOpen &&
(!GameMain.GameSession?.Campaign?.ShowCampaignUI ?? true) && !GUIMessageBox.MessageBoxes.Any(msgBox => msgBox is GUIMessageBox { MessageBoxType: GUIMessageBox.Type.Default }))
@@ -815,7 +839,7 @@ namespace Barotrauma.Items.Components
keyboardInput = Vector2.Zero;
}
if (!UseAutoDocking) { return; }
if (!UseAutoDocking || DisableControls) { return; }
if (checkConnectedPortsTimer <= 0.0f)
{
@@ -871,13 +895,11 @@ namespace Barotrauma.Items.Components
posToMaintain = item.Submarine.WorldPosition;
}
MaintainPos = true;
if (userdata is Vector2)
if (userdata is Vector2 nudgeAmount)
{
Sonar sonar = item.GetComponent<Sonar>();
Vector2 nudgeAmount = (Vector2)userdata;
if (sonar != null)
if (item.GetComponent<Sonar>() is Sonar sonar)
{
nudgeAmount *= sonar == null ? 500.0f : 500.0f / sonar.Zoom;
nudgeAmount *= 500.0f / sonar.Zoom;
}
PosToMaintain += nudgeAmount;
}
@@ -896,27 +918,27 @@ namespace Barotrauma.Items.Components
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
msg.Write(AutoPilot);
msg.Write(dockingNetworkMessagePending);
msg.WriteBoolean(AutoPilot);
msg.WriteBoolean(dockingNetworkMessagePending);
dockingNetworkMessagePending = false;
if (!AutoPilot)
{
//no need to write steering info if autopilot is controlling
msg.Write(steeringInput.X);
msg.Write(steeringInput.Y);
msg.WriteSingle(steeringInput.X);
msg.WriteSingle(steeringInput.Y);
}
else
{
msg.Write(posToMaintain != null);
msg.WriteBoolean(posToMaintain != null);
if (posToMaintain != null)
{
msg.Write(((Vector2)posToMaintain).X);
msg.Write(((Vector2)posToMaintain).Y);
msg.WriteSingle(((Vector2)posToMaintain).X);
msg.WriteSingle(((Vector2)posToMaintain).Y);
}
else
{
msg.Write(LevelStartSelected);
msg.WriteBoolean(LevelStartSelected);
}
}
}
@@ -1000,9 +1022,20 @@ namespace Barotrauma.Items.Components
steeringModeSwitch.Selected = AutoPilot;
autopilotIndicator.Selected = AutoPilot;
manualPilotIndicator.Selected = !AutoPilot;
maintainPosTickBox.Enabled = AutoPilot;
levelEndTickBox.Enabled = AutoPilot;
levelStartTickBox.Enabled = AutoPilot;
if (DisableControls)
{
steeringModeSwitch.Enabled = false;
maintainPosTickBox.Enabled = false;
levelEndTickBox.Enabled = false;
levelStartTickBox.Enabled = false;
}
else
{
steeringModeSwitch.Enabled = true;
maintainPosTickBox.Enabled = AutoPilot;
levelEndTickBox.Enabled = AutoPilot;
levelStartTickBox.Enabled = AutoPilot;
}
}
}
}

View File

@@ -83,7 +83,8 @@ namespace Barotrauma.Items.Components
}
};
rechargeSpeedSlider.Bar.RectTransform.MaxSize = new Point(rechargeSpeedSlider.Bar.Rect.Height);
rechargeSpeedSlider.Frame.UserData = UIHighlightAction.ElementId.RechargeSpeedSlider;
// lower area --------------------------
var chargeTextContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), lowerArea.RectTransform), style: null);

View File

@@ -1,8 +1,4 @@
using Barotrauma.Sounds;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
namespace Barotrauma.Items.Components
{
partial class Powered : ItemComponent
{

View File

@@ -22,6 +22,8 @@ namespace Barotrauma.Items.Components
private GUILayoutGroup extraButtonContainer;
private GUIComponent skillTextContainer;
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
//the corresponding particle emitter is active when the condition is within this range
private readonly List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
@@ -59,7 +61,7 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character)
{
if (item.HiddenInGame) { return false; }
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) { return false; }
if (!HasRequiredItems(character, false) || character.SelectedItem != item) { return false; }
if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
float defaultMaxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
@@ -110,6 +112,7 @@ namespace Barotrauma.Items.Components
if (GuiFrame != null)
{
GuiFrame.ClearChildren();
TryCreateDragHandle();
CreateGUI();
}
}
@@ -131,9 +134,10 @@ namespace Barotrauma.Items.Components
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
TextManager.Get("RequiredRepairSkills"), font: GUIStyle.SubHeadingFont);
skillTextContainer = paddedFrame;
for (int i = 0; i < requiredSkills.Count; i++)
{
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillTextContainer.RectTransform),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + requiredSkills[i].Identifier), ((int) Math.Round(requiredSkills[i].Level * SkillRequirementMultiplier)).ToString()),
font: GUIStyle.SmallFont)
{
@@ -161,6 +165,7 @@ namespace Barotrauma.Items.Components
repairingText = TextManager.Get("Repairing");
RepairButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), progressBarHolder.RectTransform, Anchor.TopCenter), repairButtonText)
{
UserData = UIHighlightAction.ElementId.RepairButton,
OnClicked = (btn, obj) =>
{
requestStartFixAction = FixActions.Repair;
@@ -265,7 +270,7 @@ namespace Barotrauma.Items.Components
}
}
if (CurrentFixer != null && CurrentFixer.SelectedConstruction == item)
if (CurrentFixer != null && CurrentFixer.SelectedItem == item)
{
if (repairSoundChannel == null || !repairSoundChannel.IsPlaying)
{
@@ -353,24 +358,16 @@ namespace Barotrauma.Items.Components
tinkerButtonText :
tinkeringText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
//System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
extraButtonContainer.Visible = SabotageButton.Visible || TinkerButton.Visible;
extraButtonContainer.IgnoreLayoutGroups = !extraButtonContainer.Visible;
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
foreach (GUIComponent c in skillTextContainer.Children)
{
if (!(c.UserData is Skill skill)) continue;
if (c.UserData is not Skill skill) { continue; }
GUITextBlock textBlock = (GUITextBlock)c;
if (character.GetSkillLevel(skill.Identifier) < (skill.Level * SkillRequirementMultiplier))
{
textBlock.TextColor = GUIStyle.Red;
}
else
{
textBlock.TextColor = Color.White;
}
textBlock.TextColor = character.GetSkillLevel(skill.Identifier) < (skill.Level * SkillRequirementMultiplier) ? GUIStyle.Red : GUIStyle.TextColorNormal;
}
}
@@ -450,7 +447,7 @@ namespace Barotrauma.Items.Components
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
msg.WriteRangedInteger((int)requestStartFixAction, 0, 2);
msg.Write(qteSuccess);
msg.WriteBoolean(qteSuccess);
}
}
}

View File

@@ -94,7 +94,6 @@ namespace Barotrauma.Items.Components
protected override void OnResolutionChanged()
{
base.OnResolutionChanged();
OnItemLoadedProjSpecific();
}

View File

@@ -21,12 +21,13 @@ namespace Barotrauma.Items.Components
public float FlashTimer { get; private set; }
public static Wire DraggingConnected { get; private set; }
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Character character)
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Rectangle dragArea, Character character)
{
if (DraggingConnected?.Item?.Removed ?? true)
{
DraggingConnected = null;
}
Rectangle panelRect = panel.GuiFrame.Rect;
int x = panelRect.X, y = panelRect.Y;
int width = panelRect.Width, height = panelRect.Height;
@@ -131,7 +132,10 @@ namespace Barotrauma.Items.Components
{
if (mouseInRect)
{
DrawWire(spriteBatch, DraggingConnected, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height - 10), null, panel, "");
Vector2 wireDragPos = new Vector2(
MathHelper.Clamp(PlayerInput.MousePosition.X, dragArea.X, dragArea.Right),
MathHelper.Clamp(PlayerInput.MousePosition.Y, dragArea.Y, dragArea.Bottom));
DrawWire(spriteBatch, DraggingConnected, wireDragPos, new Vector2(x + width / 2, y + height - 10), null, panel, "");
}
panel.TriggerRewiringSound();
@@ -317,6 +321,7 @@ namespace Barotrauma.Items.Components
bool mouseOn =
canDrag &&
!(GUI.MouseOn is GUIDragHandle) &&
((PlayerInput.MousePosition.X > Math.Min(start.X, end.X) &&
PlayerInput.MousePosition.X < Math.Max(start.X, end.X) &&
MathUtils.LineToPointDistanceSquared(start, end, PlayerInput.MousePosition) < 36) ||

View File

@@ -5,7 +5,6 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -27,16 +26,25 @@ namespace Barotrauma.Items.Components
private Point originalMaxSize;
private Vector2 originalRelativeSize;
private GUIComponent dragArea;
public override bool RecreateGUIOnResolutionChange => true;
partial void InitProjSpecific()
{
if (GuiFrame == null) { return; }
originalMaxSize = GuiFrame.RectTransform.MaxSize;
originalRelativeSize = GuiFrame.RectTransform.RelativeSize;
CheckForLabelOverlap();
new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform), DrawConnections, null)
var content = new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform), DrawConnections, null)
{
UserData = this
};
content.RectTransform.SetAsFirstChild();
//prevents inputs from going through the GUICustomComponent to the drag handle
dragArea = new GUIFrame(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center)
{ AbsoluteOffset = GUIStyle.ItemFrameOffset }, style: null);
}
public void TriggerRewiringSound()
@@ -62,7 +70,7 @@ namespace Barotrauma.Items.Components
}
rewireSoundTimer -= deltaTime;
if (user != null && user.SelectedConstruction == item && rewireSoundTimer > 0.0f)
if (user != null && user.SelectedItem == item && rewireSoundTimer > 0.0f)
{
if (rewireSoundChannel == null || !rewireSoundChannel.IsPlaying)
{
@@ -85,12 +93,12 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character)
{
return character == Character.Controlled && character == user && character.SelectedConstruction == item;
return character == Character.Controlled && character == user && character.SelectedItem == item;
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (character != Character.Controlled || character != user || character.SelectedConstruction != item) { return; }
if (character != Character.Controlled || character != user || character.SelectedItem != item) { return; }
if (HighlightedWire != null)
{
@@ -105,7 +113,7 @@ namespace Barotrauma.Items.Components
if (user != Character.Controlled || user == null) { return; }
HighlightedWire = null;
Connection.DrawConnections(spriteBatch, this, user);
Connection.DrawConnections(spriteBatch, this, dragArea.Rect, user);
foreach (UISprite sprite in GUIStyle.GetComponentStyle("ConnectionPanelFront").Sprites[GUIComponent.ComponentState.None])
{
@@ -115,7 +123,6 @@ namespace Barotrauma.Items.Components
protected override void OnResolutionChanged()
{
base.OnResolutionChanged();
if (GuiFrame == null) { return; }
CheckForLabelOverlap();
}

View File

@@ -14,6 +14,8 @@ namespace Barotrauma.Items.Components
private Point ElementMaxSize => new Point(uiElementContainer.Rect.Width, (int)(65 * GUI.yScale));
public override bool RecreateGUIOnResolutionChange => true;
partial void InitProjSpecific()
{
CreateGUI();
@@ -133,7 +135,7 @@ namespace Barotrauma.Items.Components
}
else
{
DebugConsole.ShowError($"Error creating a CustomInterface component: unexpected NumberType \"{(ciElement.NumberType.HasValue ? ciElement.NumberType.Value.ToString() : "none")}\"");
DebugConsole.LogError($"Error creating a CustomInterface component: unexpected NumberType \"{(ciElement.NumberType.HasValue ? ciElement.NumberType.Value.ToString() : "none")}\"");
}
if (numberInput != null)
{
@@ -354,29 +356,29 @@ namespace Barotrauma.Items.Components
{
if (!element.IsNumberInput)
{
msg.Write(((GUITextBox)uiElements[i]).Text);
msg.WriteString(((GUITextBox)uiElements[i]).Text);
}
else
{
switch (element.NumberType)
{
case NumberType.Float:
msg.Write(((GUINumberInput)uiElements[i]).FloatValue.ToString());
msg.WriteString(((GUINumberInput)uiElements[i]).FloatValue.ToString());
break;
case NumberType.Int:
default:
msg.Write(((GUINumberInput)uiElements[i]).IntValue.ToString());
msg.WriteString(((GUINumberInput)uiElements[i]).IntValue.ToString());
break;
}
}
}
else if (element.ContinuousSignal)
{
msg.Write(((GUITickBox)uiElements[i]).Selected);
msg.WriteBoolean(((GUITickBox)uiElements[i]).Selected);
}
else
{
msg.Write(extraData is Item.ComponentStateEventData { ComponentData: EventData eventData } && eventData.BtnElement == customInterfaceElementList[i]);
msg.WriteBoolean(extraData is Item.ComponentStateEventData { ComponentData: EventData eventData } && eventData.BtnElement == customInterfaceElementList[i]);
}
}
}

View File

@@ -149,7 +149,7 @@ namespace Barotrauma.Items.Components
{
if (TryExtractEventData(extraData, out ClientEventData eventData))
{
msg.Write(eventData.Text);
msg.WriteString(eventData.Text);
}
}

View File

@@ -311,7 +311,7 @@ namespace Barotrauma.Items.Components
Wire equippedWire = Character.Controlled.HeldItems.FirstOrDefault(it => it.GetComponent<Wire>() != null)?.GetComponent<Wire>();
if (equippedWire != null && GUI.MouseOn == null)
{
if (PlayerInput.PrimaryMouseButtonClicked() && Character.Controlled.SelectedConstruction == null)
if (PlayerInput.PrimaryMouseButtonClicked() && Character.Controlled.SelectedItem == null)
{
equippedWire.Use(1.0f, Character.Controlled);
}
@@ -603,11 +603,11 @@ namespace Barotrauma.Items.Components
{
var eventData = ExtractEventData<ClientEventData>(extraData);
int nodeCount = eventData.NodeCount;
msg.Write((byte)nodeCount);
msg.WriteByte((byte)nodeCount);
if (nodeCount > 0)
{
msg.Write(nodes.Last().X);
msg.Write(nodes.Last().Y);
msg.WriteSingle(nodes.Last().X);
msg.WriteSingle(nodes.Last().Y);
}
}
}

View File

@@ -5,9 +5,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -94,14 +92,6 @@ namespace Barotrauma.Items.Components
private set;
}
[Serialize(false, IsPropertySaveable.No, description: "Use firing offset for muzzleflash? This field shouldn't be needed but I'm using it for prototyping")]
public bool UseFiringOffsetForMuzzleFlash
{
get;
private set;
}
public Vector2 DrawSize
{
get
@@ -188,7 +178,7 @@ namespace Barotrauma.Items.Components
recoilTimer /= 1 + user.GetStatValue(StatTypes.TurretAttackSpeed);
}
PlaySound(ActionType.OnUse);
Vector2 particlePos = GetRelativeFiringPosition(UseFiringOffsetForMuzzleFlash);
Vector2 particlePos = GetRelativeFiringPosition();
foreach (ParticleEmitter emitter in particleEmitters)
{
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: -rotation, particleRotation: rotation);
@@ -248,7 +238,6 @@ namespace Barotrauma.Items.Components
{
moveSoundChannel.FadeOutAndDispose();
moveSoundChannel = null;
}
}
}

View File

@@ -26,7 +26,7 @@ namespace Barotrauma.Items.Components
{
foreach (DamageModifier damageModifier in damageModifiers)
{
if (MathUtils.NearlyEqual(damageModifier.DamageMultiplier, 1f))
if (MathUtils.NearlyEqual(damageModifier.DamageMultiplier * damageModifier.ProbabilityMultiplier, 1f))
{
continue;
}

View File

@@ -185,7 +185,7 @@ namespace Barotrauma.Items.Components
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
msg.Write((byte)allowOutpostAutoDocking);
msg.WriteByte((byte)allowOutpostAutoDocking);
}
}
}

View File

@@ -364,7 +364,7 @@ namespace Barotrauma
get
{
return Character.Controlled != null &&
Character.Controlled.SelectedConstruction == null &&
!Character.Controlled.HasSelectedAnyItem &&
CharacterHealth.OpenHealthWindow == null &&
DraggingItems.Any();
}
@@ -924,9 +924,9 @@ namespace Barotrauma
}
}
if (Character.Controlled.SelectedConstruction != null)
if (Character.Controlled.SelectedItem != null)
{
foreach (var ic in Character.Controlled.SelectedConstruction.ActiveHUDs)
foreach (var ic in Character.Controlled.SelectedItem.ActiveHUDs)
{
var itemContainer = ic as ItemContainer;
if (itemContainer?.Inventory?.visualSlots == null) { continue; }
@@ -1003,9 +1003,9 @@ namespace Barotrauma
}
}
if (character.SelectedConstruction != null)
if (character.SelectedItem != null)
{
foreach (var ic in character.SelectedConstruction.ActiveHUDs)
foreach (var ic in character.SelectedItem.ActiveHUDs)
{
var itemContainer = ic as ItemContainer;
if (itemContainer?.Inventory?.visualSlots == null) { continue; }
@@ -1147,15 +1147,16 @@ namespace Barotrauma
{
Character.Controlled.ClearInputs();
bool mouseOnPortrait = CharacterHUD.MouseOnCharacterPortrait();
if (!DetermineMouseOnInventory(ignoreDraggedItem: true) &&
CharacterHealth.OpenHealthWindow != null)
(CharacterHealth.OpenHealthWindow != null || mouseOnPortrait))
{
bool dropSuccessful = false;
foreach (Item item in DraggingItems)
{
var inventory = item.ParentInventory;
var indices = inventory?.FindIndices(item);
dropSuccessful |= CharacterHealth.OpenHealthWindow.OnItemDropped(item, false);
dropSuccessful |= (CharacterHealth.OpenHealthWindow ?? Character.Controlled.CharacterHealth).OnItemDropped(item, ignoreMousePos: mouseOnPortrait);
if (dropSuccessful)
{
if (indices != null && inventory.visualSlots != null)
@@ -1167,7 +1168,6 @@ namespace Barotrauma
}
break;
}
}
if (dropSuccessful)
{
@@ -1341,27 +1341,29 @@ namespace Barotrauma
}
else
{
var rootOwner = (selectedSlot.ParentInventory?.Owner as Item)?.GetRootInventoryOwner();
if (selectedSlot.ParentInventory?.Owner != Character.Controlled &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedCharacter &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(selectedSlot.ParentInventory?.Owner) ?? false) &&
rootOwner != Character.Controlled &&
rootOwner != Character.Controlled.SelectedCharacter &&
rootOwner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(rootOwner) ?? false))
static bool OwnerInaccessible(Entity owner) =>
owner != Character.Controlled &&
owner != Character.Controlled.SelectedCharacter &&
owner != Character.Controlled.SelectedItem &&
(Character.Controlled.SelectedItem == null || !Character.Controlled.SelectedItem.linkedTo.Contains(owner));
Entity owner = selectedSlot.ParentInventory?.Owner;
Entity rootOwner = (owner as Item)?.GetRootInventoryOwner();
if (OwnerInaccessible(owner) && (rootOwner == owner || OwnerInaccessible(rootOwner)))
{
return false;
}
var parentItem = (selectedSlot?.ParentInventory?.Owner as Item) ?? selectedSlot?.Item;
if ((parentItem?.GetRootInventoryOwner() is Character ownerCharacter) &&
ownerCharacter == Character.Controlled &&
CharacterHealth.OpenHealthWindow?.Character != ownerCharacter &&
ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface) &&
Screen.Selected != GameMain.SubEditorScreen)
Item parentItem = (owner as Item) ?? selectedSlot?.Item;
if (parentItem?.GetRootInventoryOwner() is Character ownerCharacter)
{
highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem);
return false;
if (ownerCharacter == Character.Controlled &&
CharacterHealth.OpenHealthWindow?.Character != ownerCharacter &&
ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface) &&
Screen.Selected != GameMain.SubEditorScreen)
{
highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem);
return false;
}
}
}
return true;
@@ -1442,7 +1444,10 @@ namespace Barotrauma
float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f);
Vector2 itemPos = PlayerInput.MousePosition;
bool mouseOnHealthInterface = CharacterHealth.OpenHealthWindow != null && CharacterHealth.OpenHealthWindow.MouseOnElement && DraggingItems.Any(it => it.UseInHealthInterface);
bool mouseOnHealthInterface =
(CharacterHealth.OpenHealthWindow != null && CharacterHealth.OpenHealthWindow.MouseOnElement)||
CharacterHUD.MouseOnCharacterPortrait();
mouseOnHealthInterface = mouseOnHealthInterface && DraggingItems.Any(it => it.UseInHealthInterface);
if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null)
{
@@ -1451,13 +1456,25 @@ namespace Barotrauma
Character.Controlled.FocusedItem != null ?
TextManager.GetWithVariable("PutItemIn", "[itemname]", Character.Controlled.FocusedItem.Name, FormatCapitals.Yes) :
TextManager.Get(Screen.Selected is SubEditorScreen editor && editor.EntityMenu.Rect.Contains(PlayerInput.MousePosition) ? "Delete" : "DropItem");
int textWidth = (int)Math.Max(GUIStyle.Font.MeasureString(DraggingItems.First().Name).X, GUIStyle.SmallFont.MeasureString(toolTip).X);
Vector2 nameSize = GUIStyle.Font.MeasureString(DraggingItems.First().Name);
Vector2 toolTipSize = GUIStyle.SmallFont.MeasureString(toolTip);
int textWidth = (int)Math.Max(nameSize.X, toolTipSize.X);
int textSpacing = (int)(15 * GUI.Scale);
Point shadowBorders = (new Point(40, 10)).Multiply(GUI.Scale);
Vector2 textPos = itemPos;
int textDir = textPos.X + textWidth * 1.5f > GameMain.GraphicsWidth ? -1 : 1;
int textOffset = textDir == 1 ? 0 : -1;
textPos += new Vector2((iconSize / 2 + textSpacing) * textDir, 0);
Point shadowPadding = new Point(40, 20).Multiply(GUI.Scale);
Point shadowSize = new Point(iconSize + textWidth + textSpacing, iconSize) + shadowPadding.Multiply(2);
shadowSprite.Draw(spriteBatch,
new Rectangle(itemPos.ToPoint() - new Point(iconSize / 2) - shadowBorders, new Point(iconSize + textWidth + textSpacing, iconSize) + shadowBorders.Multiply(2)), Color.Black * 0.8f);
GUI.DrawString(spriteBatch, new Vector2(itemPos.X + iconSize / 2 + textSpacing, itemPos.Y - iconSize / 2), DraggingItems.First().Name, Color.White);
GUI.DrawString(spriteBatch, new Vector2(itemPos.X + iconSize / 2 + textSpacing, itemPos.Y), toolTip,
new Rectangle(itemPos.ToPoint() - new Point((iconSize / 2 - shadowPadding.X) * textDir - shadowSize.X * textOffset, iconSize / 2 + shadowPadding.Y), shadowSize), Color.Black * 0.8f);
GUI.DrawString(spriteBatch, textPos + new Vector2(nameSize.X * textOffset, -iconSize / 2), DraggingItems.First().Name, Color.White);
GUI.DrawString(spriteBatch, textPos + new Vector2(toolTipSize.X * textOffset, 0), toolTip,
color: Character.Controlled.FocusedItem == null && !mouseOnHealthInterface ? GUIStyle.Red : Color.LightGreen,
font: GUIStyle.SmallFont);
}
@@ -1725,7 +1742,8 @@ namespace Barotrauma
if (inventory != null &&
!inventory.Locked &&
Character.Controlled?.Inventory == inventory &&
slot.InventoryKeyIndex != -1)
slot.InventoryKeyIndex != -1 &&
slot.InventoryKeyIndex < GameSettings.CurrentConfig.InventoryKeyMap.Bindings.Length)
{
spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.15f), slotHotkeySprite.SourceRect, slotColor);
GUI.DrawString(spriteBatch, rect.Location.ToVector2() + new Vector2((int)(4.25f * UIScale), (int)Math.Ceiling(-1.5f * UIScale)), GameSettings.CurrentConfig.InventoryKeyMap.Bindings[slot.InventoryKeyIndex].Name, Color.Black, font: GUIStyle.HotkeyFont);

View File

@@ -118,7 +118,7 @@ namespace Barotrauma
return GetDrawDepth(SpriteDepth + DrawDepthOffset, Sprite);
}
public Color GetSpriteColor()
public Color GetSpriteColor(bool withHighlight = false)
{
Color color = spriteColor;
if (Prefab.UseContainedSpriteColor && ownInventory != null)
@@ -129,6 +129,17 @@ namespace Barotrauma
break;
}
}
if (withHighlight)
{
if (IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen)
{
color = GUIStyle.Orange * Math.Max(GetSpriteColor().A / (float)byte.MaxValue, 0.1f);
}
else if (IsHighlighted && HighlightColor.HasValue)
{
color = Color.Lerp(color, HighlightColor.Value, (MathF.Sin((float)Timing.TotalTime * 3.0f) + 1.0f) / 2.0f);
}
}
return color;
}
@@ -281,9 +292,7 @@ namespace Barotrauma
else if (!ShowItems) { return; }
}
Color color = IsIncludedInSelection && editing ? GUIStyle.Blue : IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUIStyle.Orange * Math.Max(GetSpriteColor().A / (float) byte.MaxValue, 0.1f) : GetSpriteColor();
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
Color color = IsIncludedInSelection && editing ? GUIStyle.Blue : GetSpriteColor(withHighlight: true);
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
@@ -569,6 +578,39 @@ namespace Barotrauma
}
}
partial void Splash()
{
if (body == null || CurrentHull == null) { return; }
//create a splash particle
float massFactor = MathHelper.Clamp(body.Mass, 0.5f, 20.0f);
for (int i = 0; i < MathHelper.Clamp(Math.Abs(body.LinearVelocity.Y), 1.0f, 10.0f); i++)
{
var splash = GameMain.ParticleManager.CreateParticle("watersplash",
new Vector2(WorldPosition.X, CurrentHull.WorldSurface),
new Vector2(0.0f, Math.Abs(-body.LinearVelocity.Y * massFactor)) + Rand.Vector(Math.Abs(body.LinearVelocity.Y * 10)),
Rand.Range(0.0f, MathHelper.TwoPi), CurrentHull);
if (splash != null)
{
splash.Size *= MathHelper.Clamp(Math.Abs(body.LinearVelocity.Y) * 0.1f * massFactor, 1.0f, 4.0f);
}
}
GameMain.ParticleManager.CreateParticle("bubbles",
new Vector2(WorldPosition.X, CurrentHull.WorldSurface),
body.LinearVelocity * massFactor,
0.0f, CurrentHull);
//create a wave
if (body.LinearVelocity.Y < 0.0f)
{
int n = (int)((Position.X - CurrentHull.Rect.X) / Hull.WaveWidth);
if (n >= 0 && n < currentHull.WaveVel.Length)
{
CurrentHull.WaveVel[n] += MathHelper.Clamp(body.LinearVelocity.Y * massFactor, -5.0f, 5.0f);
}
}
SoundPlayer.PlaySplashSound(WorldPosition, Math.Abs(body.LinearVelocity.Y) + Rand.Range(-10.0f, -5.0f));
}
public void CheckNeedsSoundUpdate(ItemComponent ic)
{
if (ic.NeedsSoundUpdate())
@@ -790,7 +832,7 @@ namespace Barotrauma
reloadTextureButton.OnClicked += (button, data) =>
{
Sprite.ReloadXML();
Sprite.ReloadTexture(updateAllSprites: true);
Sprite.ReloadTexture();
return true;
};
}
@@ -976,13 +1018,13 @@ namespace Barotrauma
/// <summary>
/// Reposition currently active item interfaces to make sure they don't overlap with each other
/// </summary>
private void SetHUDLayout()
private void SetHUDLayout(bool ignoreLocking = false)
{
//reset positions first
List<GUIComponent> elementsToMove = new List<GUIComponent>();
if (editingHUD != null && editingHUD.UserData == this &&
((HasInGameEditableProperties && Character.Controlled?.SelectedConstruction == this) || Screen.Selected == GameMain.SubEditorScreen))
((HasInGameEditableProperties && Character.Controlled?.SelectedItem == this) || Screen.Selected == GameMain.SubEditorScreen))
{
elementsToMove.Add(editingHUD);
}
@@ -991,6 +1033,7 @@ namespace Barotrauma
foreach (ItemComponent ic in activeHUDs)
{
if (ic.GuiFrame == null || ic.AllowUIOverlap || ic.GetLinkUIToComponent() != null) { continue; }
if (!ignoreLocking && ic.LockGuiFramePosition) { continue; }
//if the frame covers nearly all of the screen, don't trying to prevent overlaps because it'd fail anyway
if (ic.GuiFrame.Rect.Width >= GameMain.GraphicsWidth * 0.9f && ic.GuiFrame.Rect.Height >= GameMain.GraphicsHeight * 0.9f) { continue; }
ic.GuiFrame.RectTransform.ScreenSpaceOffset = Point.Zero;
@@ -1015,11 +1058,7 @@ namespace Barotrauma
disallowedAreas.Add(editor.ToggleEntityMenuButton.Rect);
}
GUI.PreventElementOverlap(elementsToMove, disallowedAreas,
new Rectangle(
0, 20,
GameMain.GraphicsWidth,
HUDLayoutSettings.InventoryTopY > 0 ? HUDLayoutSettings.InventoryTopY - 40 : GameMain.GraphicsHeight - 80));
GUI.PreventElementOverlap(elementsToMove, disallowedAreas, clampArea: HUDLayoutSettings.ItemHUDArea);
//System.Diagnostics.Debug.WriteLine("after: " + elementsToMove[0].Rect.ToString() + " " + elementsToMove[1].Rect.ToString());
foreach (ItemComponent ic in activeHUDs)
@@ -1042,7 +1081,7 @@ namespace Barotrauma
public void UpdateHUD(Camera cam, Character character, float deltaTime)
{
bool editingHUDCreated = false;
if ((HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped)) ||
if ((HasInGameEditableProperties && (character.SelectedItem == this || EditableWhenEquipped)) ||
Screen.Selected == GameMain.SubEditorScreen)
{
GUIComponent prevEditingHUD = editingHUD;
@@ -1126,7 +1165,7 @@ namespace Barotrauma
foreach (Character otherCharacter in Character.CharacterList)
{
if (otherCharacter != character &&
otherCharacter.SelectedConstruction == this)
otherCharacter.SelectedItem == this)
{
ItemInUseWarning.Visible = true;
if (mergedHUDRect.Width > GameMain.GraphicsWidth / 2) { mergedHUDRect.Inflate(-GameMain.GraphicsWidth / 4, 0); }
@@ -1145,7 +1184,7 @@ namespace Barotrauma
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
{
if (HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped))
if (HasInGameEditableProperties && (character.SelectedItem == this || EditableWhenEquipped))
{
DrawEditing(spriteBatch, cam);
}
@@ -1215,6 +1254,7 @@ namespace Barotrauma
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
Color color = Color.Gray;
if (ic.HasRequiredItems(character, false))
@@ -1238,6 +1278,24 @@ namespace Barotrauma
return texts;
}
public void ForceHUDLayoutUpdate(bool ignoreLocking = false)
{
foreach (ItemComponent ic in activeHUDs)
{
if (ic.GuiFrame == null || !ic.CanBeSelected) { continue; }
ic.GuiFrame.RectTransform.ScreenSpaceOffset = Point.Zero;
if (ic.UseAlternativeLayout)
{
ic.AlternativeLayout?.ApplyTo(ic.GuiFrame.RectTransform);
}
else
{
ic.DefaultLayout?.ApplyTo(ic.GuiFrame.RectTransform);
}
}
SetHUDLayout(ignoreLocking);
}
public override void AddToGUIUpdateList(int order = 0)
{
if (Screen.Selected is SubEditorScreen)
@@ -1246,15 +1304,15 @@ namespace Barotrauma
}
else
{
if (HasInGameEditableProperties && Character.Controlled != null && (Character.Controlled.SelectedConstruction == this || EditableWhenEquipped))
if (HasInGameEditableProperties && Character.Controlled != null && (Character.Controlled.SelectedItem == this || EditableWhenEquipped))
{
if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(); }
}
}
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this && GetComponent<RemoteController>() == null)
if (Character.Controlled != null && Character.Controlled.SelectedItem != this && GetComponent<RemoteController>() == null)
{
if (Character.Controlled.SelectedConstruction?.GetComponent<RemoteController>()?.TargetItem != this &&
if (Character.Controlled.SelectedItem?.GetComponent<RemoteController>()?.TargetItem != this &&
!Character.Controlled.HeldItems.Any(it => it.GetComponent<RemoteController>()?.TargetItem == this))
{
return;
@@ -1423,8 +1481,8 @@ namespace Barotrauma
case TreatmentEventData treatmentEventData:
Character targetCharacter = treatmentEventData.TargetCharacter;
msg.Write(targetCharacter.ID);
msg.Write(treatmentEventData.LimbIndex);
msg.WriteUInt16(targetCharacter.ID);
msg.WriteByte(treatmentEventData.LimbIndex);
break;
case ChangePropertyEventData changePropertyEventData:
WritePropertyChange(msg, changePropertyEventData, inGameEditableOnly: true);
@@ -1432,7 +1490,7 @@ namespace Barotrauma
break;
case CombineEventData combineEventData:
Item combineTarget = combineEventData.CombineTarget;
msg.Write(combineTarget.ID);
msg.WriteUInt16(combineTarget.ID);
break;
default:
throw error($"Unsupported event type {eventData.GetType().Name}");

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
@@ -17,7 +16,7 @@ namespace Barotrauma
{
if (PlayerInput.KeyHit(InputType.Select))
{
Character.Controlled.SelectedConstruction = null;
Character.Controlled.SelectedItem = null;
}
}*/
}

View File

@@ -222,13 +222,14 @@ namespace Barotrauma
if (!ResizeHorizontal && !ResizeVertical)
{
if (PlayerInput.PrimaryMouseButtonClicked())
if (PlayerInput.PrimaryMouseButtonClicked() && GUI.MouseOn == null)
{
var item = new Item(new Rectangle((int)position.X, (int)position.Y, (int)(Sprite.size.X * Scale), (int)(Sprite.size.Y * Scale)), this, Submarine.MainSub)
{
Submarine = Submarine.MainSub
};
item.SetTransform(ConvertUnits.ToSimUnits(Submarine.MainSub == null ? item.Position : item.Position - Submarine.MainSub.Position), 0.0f);
item.GetComponent<Items.Components.Door>()?.RefreshLinkedGap();
item.FindHull();
item.Submarine = Submarine.MainSub;
@@ -252,7 +253,7 @@ namespace Barotrauma
if (placePosition == Vector2.Zero)
{
if (PlayerInput.PrimaryMouseButtonHeld()) placePosition = position;
if (PlayerInput.PrimaryMouseButtonHeld() && GUI.MouseOn == null) { placePosition = position; }
}
else
{
@@ -270,11 +271,10 @@ namespace Barotrauma
item.SetTransform(ConvertUnits.ToSimUnits(Submarine.MainSub == null ? item.Position : item.Position - Submarine.MainSub.Position), 0.0f);
item.FindHull();
//selected = null;
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity> { item }, false));
return;
}
position = placePosition;
}
}
@@ -282,22 +282,12 @@ namespace Barotrauma
{
potentialContainer.IsHighlighted = true;
}
//if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed) selected = null;
}
public override void DrawPlacing(SpriteBatch spriteBatch, Camera cam)
{
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
if (PlayerInput.SecondaryMouseButtonClicked())
{
Selected = null;
return;
}
if (!ResizeHorizontal && !ResizeVertical)
{
Sprite.Draw(spriteBatch, new Vector2(position.X, -position.Y) + Sprite.size / 2.0f * Scale, SpriteColor, scale: Scale);

View File

@@ -264,10 +264,6 @@ namespace Barotrauma
{
Vector2 velocity = flowForce;
if (!IsHorizontal)
{
velocity.X = Rand.Range(-100.0f, 100.0f) * open;
}
else
{
velocity.X *= Rand.Range(1.0f, 3.0f);
}

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