(61d00a474) v0.9.7.1

This commit is contained in:
Regalis
2020-03-04 13:04:10 +01:00
parent 3c50efa5c9
commit 3c09ebe02f
5086 changed files with 786063 additions and 295871 deletions
@@ -0,0 +1,102 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
partial class Controller : ItemComponent
{
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
if (focusTarget != null && character.ViewTarget == focusTarget)
{
foreach (ItemComponent ic in focusTarget.Components)
{
ic.DrawHUD(spriteBatch, character);
}
}
}
private bool crewAreaOriginalState;
private bool chatBoxOriginalState;
private bool isHUDsHidden;
partial void HideHUDs(bool value)
{
if (isHUDsHidden == value) { return; }
if (value == true)
{
ToggleCrewArea(false, storeOriginalState: true);
ToggleChatBox(false, storeOriginalState: true);
}
else
{
ToggleCrewArea(crewAreaOriginalState, storeOriginalState: false);
ToggleChatBox(chatBoxOriginalState, storeOriginalState: false);
}
isHUDsHidden = value;
}
private void ToggleCrewArea(bool value, bool storeOriginalState)
{
var crewManager = GameMain.GameSession?.CrewManager;
if (crewManager == null) { return; }
if (storeOriginalState)
{
crewAreaOriginalState = crewManager.ToggleCrewListOpen;
}
crewManager.ToggleCrewListOpen = value;
}
private void ToggleChatBox(bool value, bool storeOriginalState)
{
var crewManager = GameMain.GameSession?.CrewManager;
if (crewManager == null) { return; }
if (crewManager.IsSinglePlayer)
{
if (crewManager.ChatBox != null)
{
if (storeOriginalState)
{
chatBoxOriginalState = crewManager.ChatBox.ToggleOpen;
}
crewManager.ChatBox.ToggleOpen = value;
}
}
else if (GameMain.Client != null)
{
if (storeOriginalState)
{
chatBoxOriginalState = GameMain.Client.ChatBox.ToggleOpen;
}
GameMain.Client.ChatBox.ToggleOpen = value;
}
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
state = msg.ReadBoolean();
ushort userID = msg.ReadUInt16();
if (userID == 0)
{
if (user != null)
{
IsActive = false;
CancelUsing(user);
user = null;
}
}
else
{
Character newUser = Entity.FindEntityByID(userID) as Character;
if (newUser != user)
{
CancelUsing(user);
}
user = newUser;
IsActive = true;
}
}
}
}
@@ -0,0 +1,179 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Deconstructor : Powered, IServerSerializable, IClientSerializable
{
public GUIButton ActivateButton
{
get { return activateButton; }
}
private GUIButton activateButton;
private GUIComponent inputInventoryHolder, outputInventoryHolder;
private GUICustomComponent inputInventoryOverlay;
private GUIComponent inSufficientPowerWarning;
private bool pendingState;
partial void InitProjSpecific(XElement element)
{
CreateGUI();
GameMain.Instance.OnResolutionChanged += RecreateGUI;
}
private void RecreateGUI()
{
GuiFrame.ClearChildren();
CreateGUI();
OnItemLoadedProjSpecific();
}
private void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.90f, 0.80f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
Stretch = true,
RelativeSpacing = 0.08f
};
var topFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), 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("uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, inputLabelArea.RectTransform), style: "HorizontalLine");
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);
inputInventoryOverlay = new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawOverLay, null) { CanBeFocused = false };
// === ACTIVATE BUTTON === //
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.7f), inputArea.RectTransform), childAnchor: Anchor.CenterLeft);
activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.8f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton")
{
TextBlock = { AutoScaleHorizontal = true },
OnClicked = ToggleActive
};
inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform),
TextManager.Get("DeconstructorNoPower"), textColor: GUI.Style.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow")
{
HoverColor = Color.Black,
IgnoreLayoutGroups = true,
Visible = false,
CanBeFocused = false
};
// === OUTPUT AREA === //
var bottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), 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)
{
Stretch = true,
RelativeSpacing = 0.05f
};
var outputLabel = new GUITextBlock(new RectTransform(new Vector2(0f, 1.0f), outputLabelArea.RectTransform), TextManager.Get("uilabel.output"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
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");
// === OUTPUT SLOTS === //
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1f), bottomFrame.RectTransform, Anchor.CenterLeft), style: null);
}
public override bool Select(Character character)
{
// TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental.
// Only do this if there's only one linked component. If you link more containers then may
// GUI.PreventElementOverlap have mercy on your HUD layout
if (item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1)
{
foreach (MapEntity linkedTo in item.linkedTo)
{
if (!(linkedTo is Item linkedItem)) continue;
if (!linkedItem.Components.Any()) continue;
var itemContainer = linkedItem.Components.First();
if (itemContainer == null) { continue; }
if (!itemContainer.Item.DisplaySideBySideWhenLinked) continue;
// how much spacing do we want between the components
var padding = (int) (8 * GUI.Scale);
// Move the linked container to the right and move the fabricator to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(GuiFrame.Rect.Width / -2 - padding, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0);
}
}
return base.Select(character);
}
partial void OnItemLoadedProjSpecific()
{
inputContainer.AllowUIOverlap = true;
inputContainer.Inventory.RectTransform = inputInventoryHolder.RectTransform;
outputContainer.AllowUIOverlap = true;
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
}
private void DrawOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{
overlayComponent.RectTransform.SetAsLastChild();
var lastSlot = inputContainer.Inventory.slots.Last();
GUI.DrawRectangle(spriteBatch,
new Rectangle(
lastSlot.Rect.X, lastSlot.Rect.Y + (int)(lastSlot.Rect.Height * (1.0f - progressState)),
lastSlot.Rect.Width, (int)(lastSlot.Rect.Height * progressState)),
GUI.Style.Green * 0.5f, isFilled: true);
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
inSufficientPowerWarning.Visible = CurrPowerConsumption > 0 && !hasPower;
}
private bool ToggleActive(GUIButton button, object obj)
{
if (GameMain.Client != null)
{
pendingState = !IsActive;
item.CreateClientEvent(this);
}
else
{
SetActive(!IsActive, Character.Controlled);
currPowerConsumption = IsActive ? powerConsumption : 0.0f;
}
return true;
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.Write(pendingState);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
SetActive(msg.ReadBoolean());
progressTimer = msg.ReadSingle();
}
protected override void RemoveComponentSpecific()
{
GameMain.Instance.OnResolutionChanged -= RecreateGUI;
}
}
}
@@ -0,0 +1,170 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Engine : Powered, IDrawableComponent
{
private float spriteIndex;
private SpriteSheet propellerSprite;
private GUITickBox powerIndicator;
private GUIScrollBar forceSlider;
private GUITickBox autoControlIndicator;
public float AnimSpeed
{
get;
private set;
}
public Vector2 DrawSize
{
//use the extents of the item as the draw size
get { return Vector2.Zero; }
}
partial void InitProjSpecific(XElement element)
{
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.85f, 0.65f), GuiFrame.RectTransform, Anchor.Center)
{
RelativeOffset = new Vector2(0, 0.04f)
}, style: null);
var lightsArea = new GUIFrame(new RectTransform(new Vector2(1, 0.38f), paddedFrame.RectTransform, Anchor.TopLeft), style: null);
powerIndicator = new GUITickBox(new RectTransform(new Vector2(0.45f, 0.8f), lightsArea.RectTransform, Anchor.Center, Pivot.CenterRight)
{
RelativeOffset = new Vector2(-0.05f, 0)
}, TextManager.Get("EnginePowered"), font: GUI.SubHeadingFont, style: "IndicatorLightGreen")
{
CanBeFocused = false
};
autoControlIndicator = new GUITickBox(new RectTransform(new Vector2(0.45f, 0.8f), lightsArea.RectTransform, Anchor.Center, Pivot.CenterLeft)
{
RelativeOffset = new Vector2(0.05f, 0)
}, TextManager.Get("PumpAutoControl", fallBackTag: "ReactorAutoControl"), font: GUI.SubHeadingFont, style: "IndicatorLightYellow")
{
CanBeFocused = false
};
powerIndicator.TextBlock.Wrap = autoControlIndicator.TextBlock.Wrap = true;
powerIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);
autoControlIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);
GUITextBlock.AutoScaleAndNormalize(powerIndicator.TextBlock, autoControlIndicator.TextBlock);
var sliderArea = new GUIFrame(new RectTransform(new Vector2(1, 0.6f), paddedFrame.RectTransform, Anchor.BottomLeft), style: null);
string powerLabel = TextManager.Get("EngineForce");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), sliderArea.RectTransform, Anchor.TopCenter), "", textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.Center)
{
AutoScaleHorizontal = true,
TextGetter = () => { return TextManager.AddPunctuation(':', powerLabel, (int)(targetForce) + " %"); }
};
forceSlider = new GUIScrollBar(new RectTransform(new Vector2(0.95f, 0.45f), sliderArea.RectTransform, Anchor.Center), barSize: 0.1f, style: "DeviceSlider")
{
Step = 0.05f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
float newTargetForce = barScroll * 200.0f - 100.0f;
if (Math.Abs(newTargetForce - targetForce) < 0.01) { return false; }
targetForce = newTargetForce;
if (GameMain.Client != null)
{
correctionTimer = CorrectionDelay;
item.CreateClientEvent(this);
}
return true;
}
};
var textsArea = new GUIFrame(new RectTransform(new Vector2(1, 0.25f), sliderArea.RectTransform, Anchor.BottomCenter), style: null);
var backwardsLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1.0f), textsArea.RectTransform, Anchor.CenterLeft), TextManager.Get("EngineBackwards"),
textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
var forwardsLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1.0f), textsArea.RectTransform, Anchor.CenterRight), TextManager.Get("EngineForwards"),
textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight);
GUITextBlock.AutoScaleAndNormalize(backwardsLabel, forwardsLabel);
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "propellersprite":
propellerSprite = new SpriteSheet(subElement);
AnimSpeed = subElement.GetAttributeFloat("animspeed", 1.0f);
break;
}
}
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
powerIndicator.Selected = hasPower && IsActive;
autoControlIndicator.Selected = controlLockTimer > 0.0f;
forceSlider.Enabled = controlLockTimer <= 0.0f;
if (!PlayerInput.PrimaryMouseButtonHeld())
{
float newScroll = (targetForce + 100.0f) / 200.0f;
if (Math.Abs(newScroll - forceSlider.BarScroll) > 0.01f)
{
forceSlider.BarScroll = newScroll;
}
}
}
partial void UpdateAnimation(float deltaTime)
{
if (propellerSprite == null) { return; }
spriteIndex += (force / 100.0f) * AnimSpeed * deltaTime;
if (spriteIndex < 0)
{
spriteIndex = propellerSprite.FrameCount;
}
if (spriteIndex >= propellerSprite.FrameCount)
{
spriteIndex = 0.0f;
}
}
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
if (propellerSprite != null)
{
Vector2 drawPos = item.DrawPosition;
drawPos += PropellerPos;
drawPos.Y = -drawPos.Y;
propellerSprite.Draw(spriteBatch, (int)Math.Floor(spriteIndex), drawPos, Color.White, propellerSprite.Origin, 0.0f, Vector2.One);
}
if (editing)
{
Vector2 drawPos = item.DrawPosition;
drawPos += PropellerPos;
drawPos.Y = -drawPos.Y;
GUI.DrawRectangle(spriteBatch, drawPos - Vector2.One * 10, Vector2.One * 20, GUI.Style.Red);
}
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
//targetForce can only be adjusted at 10% intervals -> no need for more accuracy than this
msg.WriteRangedInteger((int)(targetForce / 10.0f), -10, 10);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{
StartDelayedCorrection(type, msg.ExtractBits(5), sendingTime);
return;
}
targetForce = msg.ReadRangedInteger(-10, 10) * 10.0f;
}
}
}
@@ -0,0 +1,613 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
namespace Barotrauma.Items.Components
{
partial class Fabricator : Powered, IServerSerializable, IClientSerializable
{
private GUIListBox itemList;
private GUIFrame selectedItemFrame;
private GUIFrame selectedItemReqsFrame;
public GUIButton ActivateButton
{
get { return activateButton; }
}
private GUIButton activateButton;
private GUITextBox itemFilterBox;
private GUIComponent outputSlot;
private GUIComponent inputInventoryHolder, outputInventoryHolder;
public FabricationRecipe SelectedItem
{
get { return selectedItem; }
}
private FabricationRecipe selectedItem;
private GUIComponent inSufficientPowerWarning;
private FabricationRecipe pendingFabricatedItem;
private Pair<Rectangle, string> tooltip;
partial void InitProjSpecific()
{
CreateGUI();
GameMain.Instance.OnResolutionChanged += RecreateGUI;
}
private void RecreateGUI()
{
GuiFrame.ClearChildren();
CreateGUI();
OnItemLoadedProjSpecific();
}
private void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter);
// === LABEL === //
new GUITextBlock(new RectTransform(new Vector2(1f, 0.05f), paddedFrame.RectTransform), item.Name, font: GUI.SubHeadingFont)
{
TextAlignment = Alignment.Center,
AutoScaleVertical = true
};
var mainFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), paddedFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
RelativeSpacing = 0.02f
};
// === TOP AREA ===
var topFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.65f), mainFrame.RectTransform), style: "InnerFrameDark");
// === ITEM LIST ===
var itemListFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), topFrame.RectTransform), childAnchor: Anchor.Center);
var paddedItemFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), itemListFrame.RectTransform))
{
Stretch = true,
RelativeSpacing = 0.03f
};
var filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), paddedItemFrame.RectTransform), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.03f,
UserData = "filterarea"
};
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.SubHeadingFont)
{
Padding = Vector4.Zero,
AutoScaleVertical = true
};
itemFilterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), createClearButton: true);
itemFilterBox.OnTextChanged += (textBox, text) =>
{
FilterEntities(text);
return true;
};
filterArea.RectTransform.MaxSize = new Point(int.MaxValue, itemFilterBox.Rect.Height);
itemList = new GUIListBox(new RectTransform(new Vector2(1f, 0.9f), paddedItemFrame.RectTransform), style: null)
{
OnSelected = (component, userdata) =>
{
selectedItem = userdata as FabricationRecipe;
if (selectedItem != null) SelectItem(Character.Controlled, selectedItem);
return true;
}
};
// === SEPARATOR === //
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), topFrame.RectTransform, Anchor.Center), style: "VerticalLine");
// === OUTPUT AREA === //
var outputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1f), topFrame.RectTransform, Anchor.TopRight), childAnchor: Anchor.Center);
var paddedOutputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), outputArea.RectTransform));
var outputTopArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5F), paddedOutputArea.RectTransform, Anchor.Center), isHorizontal: true);
// === OUTPUT SLOT === //
outputSlot = new GUIFrame(new RectTransform(new Vector2(0.4f, 1f), outputTopArea.RectTransform), style: null);
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.2f), outputSlot.RectTransform, Anchor.BottomCenter), style: null);
new GUICustomComponent(new RectTransform(Vector2.One, outputInventoryHolder.RectTransform), DrawOutputOverLay) { CanBeFocused = false };
// === DESCRIPTION === //
selectedItemFrame = new GUIFrame(new RectTransform(new Vector2(0.6f, 1f), outputTopArea.RectTransform), style: null);
// === REQUIREMENTS === //
selectedItemReqsFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedOutputArea.RectTransform), style: null);
// === BOTTOM AREA === //
var bottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.3f), mainFrame.RectTransform), style: null);
// === SEPARATOR === //
var separatorArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.15f), bottomFrame.RectTransform, Anchor.TopCenter), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.03f
};
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, separatorArea.RectTransform), TextManager.Get("uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, separatorArea.RectTransform), style: "HorizontalLine");
// === INPUT AREA === //
var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 1f), bottomFrame.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomLeft);
// === INPUT SLOTS === //
inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.7f, 1f), inputArea.RectTransform), style: null);
new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawInputOverLay) { CanBeFocused = false };
// === ACTIVATE BUTTON === //
var buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterRight);
activateButton = new GUIButton(new RectTransform(new Vector2(1f, 0.6f), buttonFrame.RectTransform),
TextManager.Get("FabricatorCreate"), style: "DeviceButton")
{
OnClicked = StartButtonClicked,
UserData = selectedItem,
Enabled = false
};
// === POWER WARNING === //
inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform),
TextManager.Get("FabricatorNoPower"), textColor: GUI.Style.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow", wrap: true)
{
HoverColor = Color.Black,
IgnoreLayoutGroups = true,
Visible = false,
CanBeFocused = false
};
CreateRecipes();
}
partial void CreateRecipes()
{
itemList.Content.RectTransform.ClearChildren();
foreach (FabricationRecipe fi in fabricationRecipes)
{
var frame = new GUIFrame(new RectTransform(new Point(itemList.Rect.Width, (int)(40 * GUI.yScale)), itemList.Content.RectTransform), style: null)
{
UserData = fi,
HoverColor = Color.Gold * 0.2f,
SelectedColor = Color.Gold * 0.5f,
ToolTip = fi.TargetItem.Description
};
var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform),
childAnchor: Anchor.CenterLeft, isHorizontal: true) { RelativeSpacing = 0.02f };
var itemIcon = fi.TargetItem.InventoryIcon ?? fi.TargetItem.sprite;
if (itemIcon != null)
{
new GUIImage(new RectTransform(new Point(frame.Rect.Height,frame.Rect.Height), container.RectTransform),
itemIcon, scaleToFit: true)
{
Color = fi.TargetItem.InventoryIconColor,
ToolTip = fi.TargetItem.Description
};
}
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform), fi.DisplayName)
{
Padding = Vector4.Zero,
AutoScaleVertical = true,
ToolTip = fi.TargetItem.Description
};
}
}
partial void OnItemLoadedProjSpecific()
{
inputContainer.AllowUIOverlap = true;
inputContainer.Inventory.RectTransform = inputInventoryHolder.RectTransform;
outputContainer.AllowUIOverlap = true;
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
}
partial void SelectProjSpecific(Character character)
{
// TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental.
// Only do this if there's only one linked component. If you link more containers then may
// GUI.PreventElementOverlap have mercy on your HUD layout
if (item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1)
{
foreach (MapEntity linkedTo in item.linkedTo)
{
if (!(linkedTo is Item linkedItem)) continue;
if (!linkedItem.Components.Any()) continue;
var itemContainer = linkedItem.Components.First();
if (itemContainer == null) { continue; }
if (!itemContainer.Item.DisplaySideBySideWhenLinked) continue;
// how much spacing do we want between the components
var padding = (int) (8 * GUI.Scale);
// Move the linked container to the right and move the fabricator to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(GuiFrame.Rect.Width / -2 - padding, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0);
}
}
var nonItems = itemList.Content.Children.Where(c => !(c.UserData is FabricationRecipe)).ToList();
nonItems.ForEach(i => itemList.Content.RemoveChild(i));
itemList.Content.RectTransform.SortChildren((c1, c2) =>
{
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
bool hasSkills1 = DegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f;
bool hasSkills2 = DegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f;
if (hasSkills1 != hasSkills2)
{
return hasSkills1 ? -1 : 1;
}
return string.Compare(item1.DisplayName, item2.DisplayName);
});
var sufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorsufficientskills", returnNull: true) ?? "Sufficient skills to fabricate", textColor: GUI.Style.Green, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
};
sufficientSkillsText.RectTransform.SetAsFirstChild();
var insufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorinsufficientskills", returnNull: true) ?? "Insufficient skills to fabricate", textColor: Color.Orange, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
};
var firstinSufficient = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && DegreeOfSuccess(character, fabricableItem.RequiredSkills) < 0.5f);
if (firstinSufficient != null)
{
insufficientSkillsText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstinSufficient.RectTransform));
}
}
private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{
overlayComponent.RectTransform.SetAsLastChild();
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++)
{
missingItems.Add(requiredItem);
}
}
foreach (Item item in inputContainer.Inventory.Items)
{
if (item == null) { continue; }
missingItems.Remove(missingItems.FirstOrDefault(mi => mi.ItemPrefab == item.prefab));
}
var availableIngredients = GetAvailableIngredients();
foreach (FabricationRecipe.RequiredItem requiredItem in missingItems)
{
while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.Items[slotIndex] != null)
{
slotIndex++;
}
//highlight suitable ingredients in linked inventories
foreach (Item item in availableIngredients)
{
if (item.ParentInventory != inputContainer.Inventory && IsItemValidIngredient(item, requiredItem))
{
int availableSlotIndex = Array.IndexOf(item.ParentInventory.Items, item);
//slots are null if the inventory has never been displayed
//(linked item, but the UI is not set to be displayed at the same time)
if (item.ParentInventory.slots != null)
{
if (item.ParentInventory.slots[availableSlotIndex].HighlightTimer <= 0.0f)
{
item.ParentInventory.slots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f);
if (slotIndex < inputContainer.Capacity)
{
inputContainer.Inventory.slots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f);
}
}
}
}
}
if (slotIndex >= inputContainer.Capacity) { break; }
var itemIcon = requiredItem.ItemPrefab.InventoryIcon ?? requiredItem.ItemPrefab.sprite;
Rectangle slotRect = inputContainer.Inventory.slots[slotIndex].Rect;
itemIcon.Draw(
spriteBatch,
slotRect.Center.ToVector2(),
color: requiredItem.ItemPrefab.InventoryIconColor * 0.3f,
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
if (slotRect.Contains(PlayerInput.MousePosition))
{
string toolTipText = requiredItem.ItemPrefab.Name;
if (!string.IsNullOrEmpty(requiredItem.ItemPrefab.Description))
{
toolTipText += '\n' + requiredItem.ItemPrefab.Description;
}
tooltip = new Pair<Rectangle, string>(slotRect, toolTipText);
}
slotIndex++;
}
}
}
private void DrawOutputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{
overlayComponent.RectTransform.SetAsLastChild();
if (outputContainer.Inventory.Items.First() != null) { return; }
FabricationRecipe targetItem = fabricatedItem ?? selectedItem;
if (targetItem != null)
{
var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.sprite;
Rectangle slotRect = outputContainer.Inventory.slots[0].Rect;
if (fabricatedItem != null)
{
GUI.DrawRectangle(spriteBatch,
new Rectangle(
slotRect.X, slotRect.Y + (int)(slotRect.Height * (1.0f - progressState)),
slotRect.Width, (int)(slotRect.Height * progressState)),
GUI.Style.Green * 0.5f, isFilled: true);
}
itemIcon.Draw(
spriteBatch,
slotRect.Center.ToVector2(),
color: targetItem.TargetItem.InventoryIconColor * 0.4f,
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y) * 0.9f);
}
if (tooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
tooltip = null;
}
}
private bool FilterEntities(string filter)
{
if (string.IsNullOrWhiteSpace(filter))
{
itemList.Content.Children.ForEach(c => c.Visible = true);
return true;
}
filter = filter.ToLower();
foreach (GUIComponent child in itemList.Content.Children)
{
FabricationRecipe recipe = child.UserData as FabricationRecipe;
if (recipe?.DisplayName == null) { continue; }
child.Visible = recipe.DisplayName.ToLower().Contains(filter);
}
itemList.UpdateScrollBarSize();
itemList.BarScroll = 0.0f;
return true;
}
public bool ClearFilter()
{
FilterEntities("");
itemList.UpdateScrollBarSize();
itemList.BarScroll = 0.0f;
itemFilterBox.Text = "";
return true;
}
private bool SelectItem(Character user, FabricationRecipe selectedItem)
{
selectedItemFrame.ClearChildren();
selectedItemReqsFrame.ClearChildren();
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
var paddedReqFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemReqsFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
/*var itemIcon = selectedItem.TargetItem.InventoryIcon ?? selectedItem.TargetItem.sprite;
if (itemIcon != null)
{
GUIImage img = new GUIImage(new RectTransform(new Point(40, 40), paddedFrame.RectTransform),
itemIcon, scaleToFit: true)
{
Color = selectedItem.TargetItem.InventoryIconColor
};
}*/
var nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
selectedItem.TargetItem.Name, textAlignment: Alignment.CenterLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true
};
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, nameBlock.Padding.Z, nameBlock.Padding.W);
if (!string.IsNullOrWhiteSpace(selectedItem.TargetItem.Description))
{
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
selectedItem.TargetItem.Description,
font: GUI.SmallFont, wrap: true);
description.Padding = new Vector4(0, description.Padding.Y, description.Padding.Z, description.Padding.W);
while (description.Rect.Height + nameBlock.Rect.Height > paddedFrame.Rect.Height)
{
var lines = description.WrappedText.Split('\n');
var newString = string.Join('\n', lines.Take(lines.Length - 1));
description.Text = newString.Substring(0, newString.Length - 4) + "...";
description.CalculateHeightFromText();
description.ToolTip = selectedItem.TargetItem.Description;
}
}
List<Skill> inadequateSkills = new List<Skill>();
if (user != null)
{
inadequateSkills = selectedItem.RequiredSkills.FindAll(skill => user.GetSkillLevel(skill.Identifier) < skill.Level);
}
if (selectedItem.RequiredSkills.Any())
{
string text = "";
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform),
TextManager.Get("FabricatorRequiredSkills"), textColor: inadequateSkills.Any() ? GUI.Style.Red : GUI.Style.Green, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
};
foreach (Skill skill in selectedItem.RequiredSkills)
{
text += TextManager.Get("SkillName." + skill.Identifier) + " " + TextManager.Get("Lvl").ToLower() + " " + skill.Level;
if (skill != selectedItem.RequiredSkills.Last()) { text += "\n"; }
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), text, font: GUI.SmallFont);
}
float degreeOfSuccess = user == null ? 0.0f : DegreeOfSuccess(user, selectedItem.RequiredSkills);
if (degreeOfSuccess > 0.5f) { degreeOfSuccess = 1.0f; }
float requiredTime = user == null ? selectedItem.RequiredTime : GetRequiredTime(selectedItem, user);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform),
TextManager.Get("FabricatorRequiredTime") , textColor: ToolBox.GradientLerp(degreeOfSuccess, GUI.Style.Red, Color.Yellow, GUI.Style.Green), font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), ToolBox.SecondsToReadableTime(requiredTime),
font: GUI.SmallFont);
return true;
}
public void HighlightRecipe(string identifier, Color color)
{
foreach (GUIComponent child in itemList.Content.Children)
{
FabricationRecipe recipe = child.UserData as FabricationRecipe;
if (recipe?.DisplayName == null) { continue; }
if (recipe.TargetItem.Identifier == identifier)
{
if (child.FlashTimer > 0.0f) return;
child.Flash(color, 1.5f, false);
for (int i = 0; i < child.CountChildren; i++)
{
var grandChild = child.GetChild(i);
if (grandChild is GUITextBlock) continue;
grandChild.Flash(color, 1.5f, false);
}
return;
}
}
}
private bool StartButtonClicked(GUIButton button, object obj)
{
if (selectedItem == null) { return false; }
if (!outputContainer.Inventory.IsEmpty())
{
outputSlot.Flash(GUI.Style.Red);
return false;
}
if (GameMain.Client != null)
{
pendingFabricatedItem = fabricatedItem != null ? null : selectedItem;
item.CreateClientEvent(this);
}
else
{
if (fabricatedItem == null)
{
StartFabricating(selectedItem, Character.Controlled);
}
else
{
CancelFabricating(Character.Controlled);
}
}
return true;
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
activateButton.Enabled = false;
inSufficientPowerWarning.Visible = currPowerConsumption > 0 && !hasPower;
var availableIngredients = GetAvailableIngredients();
if (character != null)
{
foreach (GUIComponent child in itemList.Content.Children)
{
var itemPrefab = child.UserData as FabricationRecipe;
if (itemPrefab == null) continue;
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients);
if (itemPrefab == selectedItem)
{
activateButton.Enabled = canBeFabricated;
}
var childContainer = child.GetChild<GUILayoutGroup>();
childContainer.GetChild<GUITextBlock>().TextColor = Color.White * (canBeFabricated ? 1.0f : 0.5f);
childContainer.GetChild<GUIImage>().Color = itemPrefab.TargetItem.InventoryIconColor * (canBeFabricated ? 1.0f : 0.5f);
}
}
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
int itemIndex = pendingFabricatedItem == null ? -1 : fabricationRecipes.IndexOf(pendingFabricatedItem);
msg.WriteRangedInteger(itemIndex, -1, fabricationRecipes.Count - 1);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
int itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);
UInt16 userID = msg.ReadUInt16();
Character user = Entity.FindEntityByID(userID) as Character;
if (itemIndex == -1 || user == null)
{
CancelFabricating();
}
else
{
//if already fabricating the selected item, return
if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex) { return; }
if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count) { return; }
SelectItem(user, fabricationRecipes[itemIndex]);
StartFabricating(fabricationRecipes[itemIndex], user);
}
}
protected override void RemoveComponentSpecific()
{
GameMain.Instance.OnResolutionChanged -= RecreateGUI;
}
}
}
@@ -0,0 +1,331 @@
using Barotrauma.Extensions;
using FarseerPhysics;
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
{
partial class MiniMap : Powered
{
private GUIFrame submarineContainer;
private GUIFrame hullInfoFrame;
private GUITextBlock hullNameText, hullBreachText, hullAirQualityText, hullWaterText;
private string noPowerTip = "";
private readonly List<Submarine> displayedSubs = new List<Submarine>();
partial void InitProjSpecific(XElement element)
{
noPowerTip = TextManager.Get("SteeringNoPowerTip");
GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f);
GuiFrame.CanBeFocused = true;
new GUICustomComponent(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset },
DrawHUDBack, null);
submarineContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center), style: null);
new GUICustomComponent(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset },
DrawHUDFront, null)
{
CanBeFocused = false
};
hullInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.13f), GUI.Canvas, minSize: new Point(250, 150)),
style: "GUIToolTip")
{
CanBeFocused = false
};
var hullInfoContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), hullInfoFrame.RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.05f
};
hullNameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), hullInfoContainer.RectTransform), "") { Wrap = true };
hullBreachText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), hullInfoContainer.RectTransform), "") { Wrap = true };
hullAirQualityText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), hullInfoContainer.RectTransform), "") { Wrap = true };
hullWaterText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), hullInfoContainer.RectTransform), "") { Wrap = true };
hullInfoFrame.Children.ForEach(c => { c.CanBeFocused = false; c.Children.ForEach(c2 => c2.CanBeFocused = false); });
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
hullInfoFrame.AddToGUIUpdateList(order: 1);
}
public override void OnMapLoaded()
{
base.OnMapLoaded();
CreateHUD();
}
private void CreateHUD()
{
submarineContainer.ClearChildren();
if (item.Submarine == null) return;
item.Submarine.CreateMiniMap(submarineContainer);
displayedSubs.Clear();
displayedSubs.Add(item.Submarine);
displayedSubs.AddRange(item.Submarine.DockedTo);
}
public override void FlipX(bool relativeToSub)
{
CreateHUD();
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
//recreate HUD if the subs we should display have changed
if ((item.Submarine == null && displayedSubs.Count > 0) || //item not inside a sub anymore, but display is still showing subs
!displayedSubs.Contains(item.Submarine) || //current sub not displayer
item.Submarine.DockedTo.Any(s => !displayedSubs.Contains(s)) || //some of the docked subs not diplayed
displayedSubs.Any(s => s != item.Submarine && !item.Submarine.DockedTo.Contains(s))) //displaying a sub that shouldn't be displayed
{
CreateHUD();
}
float distort = 1.0f - item.Condition / item.MaxCondition;
foreach (HullData hullData in hullDatas.Values)
{
hullData.DistortionTimer -= deltaTime;
if (hullData.DistortionTimer <= 0.0f)
{
hullData.Distort = Rand.Range(0.0f, 1.0f) < distort * distort;
if (hullData.Distort)
{
hullData.Oxygen = Rand.Range(0.0f, 100.0f);
hullData.Water = Rand.Range(0.0f, 1.0f);
}
hullData.DistortionTimer = Rand.Range(1.0f, 10.0f);
}
}
}
private void DrawHUDFront(SpriteBatch spriteBatch, GUICustomComponent container)
{
if (Voltage < MinVoltage)
{
Vector2 textSize = GUI.Font.MeasureString(noPowerTip);
Vector2 textPos = GuiFrame.Rect.Center.ToVector2();
GUI.DrawString(spriteBatch, textPos - textSize / 2, noPowerTip,
GUI.Style.Orange * (float)Math.Abs(Math.Sin(Timing.TotalTime)), Color.Black * 0.8f, font: GUI.SubHeadingFont);
return;
}
foreach (GUIComponent child in submarineContainer.Children.First().Children)
{
if (child.UserData is Hull hull)
{
if (hull.Submarine == null || !hull.Submarine.IsOutpost) { continue; }
string text = TextManager.GetWithVariable("MiniMapOutpostDockingInfo", "[outpost]", hull.Submarine.Name);
Vector2 textSize = GUI.Font.MeasureString(text);
Vector2 textPos = child.Center;
if (textPos.X + textSize.X / 2 > submarineContainer.Rect.Right)
textPos.X -= ((textPos.X + textSize.X / 2) - submarineContainer.Rect.Right) + 10 * GUI.xScale;
if (textPos.X - textSize.X / 2 < submarineContainer.Rect.X)
textPos.X += (submarineContainer.Rect.X - (textPos.X - textSize.X / 2)) + 10 * GUI.xScale;
GUI.DrawString(spriteBatch, textPos - textSize / 2, text,
GUI.Style.Orange * (float)Math.Abs(Math.Sin(Timing.TotalTime)), Color.Black * 0.8f);
break;
}
}
}
private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container)
{
Hull mouseOnHull = null;
hullInfoFrame.Visible = false;
foreach (Hull hull in Hull.hullList)
{
var hullFrame = submarineContainer.Children.First().FindChild(hull);
if (hullFrame == null) { continue; }
if (GUI.MouseOn == hullFrame || hullFrame.IsParentOf(GUI.MouseOn))
{
mouseOnHull = hull;
}
if (item.Submarine == null || !hasPower)
{
hullFrame.Color = Color.DarkCyan * 0.3f;
hullFrame.Children.First().Color = Color.DarkCyan * 0.3f;
}
}
if (Voltage < MinVoltage)
{
return;
}
float scale = 1.0f;
HashSet<Submarine> subs = new HashSet<Submarine>();
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine == null) continue;
var hullFrame = submarineContainer.Children.First().FindChild(hull);
if (hullFrame == null) continue;
hullDatas.TryGetValue(hull, out HullData hullData);
if (hullData == null)
{
hullData = new HullData();
GetLinkedHulls(hull, hullData.LinkedHulls);
hullDatas.Add(hull, hullData);
}
Color neutralColor = Color.DarkCyan;
if (hull.RoomName != null)
{
if (hull.RoomName.Contains("ballast") || hull.RoomName.Contains("Ballast") ||
hull.RoomName.Contains("airlock") || hull.RoomName.Contains("Airlock"))
{
neutralColor = new Color(9, 80, 159);
}
}
if (hullData.Distort)
{
hullFrame.Children.First().Color = Color.Lerp(Color.Black, Color.DarkGray * 0.5f, Rand.Range(0.0f, 1.0f));
hullFrame.Color = neutralColor * 0.5f;
continue;
}
subs.Add(hull.Submarine);
scale = Math.Min(
hullFrame.Parent.Rect.Width / (float)hull.Submarine.Borders.Width,
hullFrame.Parent.Rect.Height / (float)hull.Submarine.Borders.Height);
Color borderColor = neutralColor;
float? gapOpenSum = 0.0f;
if (ShowHullIntegrity)
{
gapOpenSum = hull.ConnectedGaps.Where(g => !g.IsRoomToRoom).Sum(g => g.Open);
borderColor = Color.Lerp(neutralColor, GUI.Style.Red, Math.Min((float)gapOpenSum, 1.0f));
}
float? oxygenAmount = null;
if (!RequireOxygenDetectors || hullData?.Oxygen != null)
{
oxygenAmount = RequireOxygenDetectors ? hullData.Oxygen : hull.OxygenPercentage;
GUI.DrawRectangle(
spriteBatch, hullFrame.Rect,
Color.Lerp(GUI.Style.Red * 0.5f, GUI.Style.Green * 0.3f, (float)oxygenAmount / 100.0f),
true);
}
float? waterAmount = null;
if (!RequireWaterDetectors || hullData.Water != null)
{
waterAmount = RequireWaterDetectors ? hullData.Water : Math.Min(hull.WaterVolume / hull.Volume, 1.0f);
if (hullFrame.Rect.Height * waterAmount > 3.0f)
{
Rectangle waterRect = new Rectangle(
hullFrame.Rect.X, (int)(hullFrame.Rect.Y + hullFrame.Rect.Height * (1.0f - waterAmount)),
hullFrame.Rect.Width, (int)(hullFrame.Rect.Height * waterAmount));
waterRect.Inflate(-3, -3);
GUI.DrawRectangle(spriteBatch, waterRect, new Color(85, 136, 147), true);
GUI.DrawLine(spriteBatch, new Vector2(waterRect.X, waterRect.Y), new Vector2(waterRect.Right, waterRect.Y), Color.LightBlue);
}
}
if (mouseOnHull == hull ||
hullData.LinkedHulls.Contains(mouseOnHull))
{
borderColor = Color.Lerp(borderColor, Color.White, 0.5f);
hullFrame.Children.First().Color = Color.White;
hullFrame.Color = borderColor;
}
else
{
hullFrame.Children.First().Color = neutralColor * 0.8f;
}
if (mouseOnHull == hull)
{
hullInfoFrame.RectTransform.ScreenSpaceOffset = hullFrame.Rect.Center;
if (hullInfoFrame.Rect.Right > GameMain.GraphicsWidth) { hullInfoFrame.RectTransform.ScreenSpaceOffset -= new Point(hullInfoFrame.Rect.Width, 0); }
if (hullInfoFrame.Rect.Bottom > GameMain.GraphicsHeight) { hullInfoFrame.RectTransform.ScreenSpaceOffset -= new Point(0, hullInfoFrame.Rect.Height); }
hullInfoFrame.Visible = true;
hullNameText.Text = hull.DisplayName;
foreach (Hull linkedHull in hullData.LinkedHulls)
{
gapOpenSum += linkedHull.ConnectedGaps.Where(g => !g.IsRoomToRoom).Sum(g => g.Open);
oxygenAmount += linkedHull.OxygenPercentage;
waterAmount += Math.Min(linkedHull.WaterVolume / linkedHull.Volume, 1.0f);
}
oxygenAmount /= (hullData.LinkedHulls.Count + 1);
waterAmount /= (hullData.LinkedHulls.Count + 1);
hullBreachText.Text = gapOpenSum > 0.1f ? TextManager.Get("MiniMapHullBreach") : "";
hullBreachText.TextColor = GUI.Style.Red;
hullAirQualityText.Text = oxygenAmount == null ? TextManager.Get("MiniMapAirQualityUnavailable") :
TextManager.AddPunctuation(':', TextManager.Get("MiniMapAirQuality"), + (int)oxygenAmount + " %");
hullAirQualityText.TextColor = oxygenAmount == null ? GUI.Style.Red : Color.Lerp(GUI.Style.Red, Color.LightGreen, (float)oxygenAmount / 100.0f);
hullWaterText.Text = waterAmount == null ? TextManager.Get("MiniMapWaterLevelUnavailable") :
TextManager.AddPunctuation(':', TextManager.Get("MiniMapWaterLevel"), (int)(waterAmount * 100.0f) + " %");
hullWaterText.TextColor = waterAmount == null ? GUI.Style.Red : Color.Lerp(Color.LightGreen, GUI.Style.Red, (float)waterAmount);
}
hullFrame.Color = borderColor;
}
foreach (Submarine sub in subs)
{
if (sub.HullVertices == null) { continue; }
Rectangle worldBorders = sub.GetDockedBorders();
worldBorders.Location += sub.WorldPosition.ToPoint();
scale = Math.Min(
submarineContainer.Rect.Width / (float)worldBorders.Width,
submarineContainer.Rect.Height / (float)worldBorders.Height) * 0.9f;
float displayScale = ConvertUnits.ToDisplayUnits(scale);
Vector2 offset = ConvertUnits.ToSimUnits(sub.WorldPosition - new Vector2(worldBorders.Center.X, worldBorders.Y - worldBorders.Height / 2));
Vector2 center = container.Rect.Center.ToVector2();
for (int i = 0; i < sub.HullVertices.Count; i++)
{
Vector2 start = (sub.HullVertices[i] + offset) * displayScale;
start.Y = -start.Y;
Vector2 end = (sub.HullVertices[(i + 1) % sub.HullVertices.Count] + offset) * displayScale;
end.Y = -end.Y;
GUI.DrawLine(spriteBatch, center + start, center + end, Color.DarkCyan * Rand.Range(0.3f, 0.35f), width: (int)(10 * GUI.Scale));
}
}
}
private void GetLinkedHulls(Hull hull, List<Hull> linkedHulls)
{
foreach (var linkedEntity in hull.linkedTo)
{
if (linkedEntity is Hull linkedHull)
{
if (linkedHulls.Contains(linkedHull)) { continue; }
linkedHulls.Add(linkedHull);
GetLinkedHulls(linkedHull, linkedHulls);
}
}
}
}
}
@@ -0,0 +1,213 @@
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
{
partial class Pump : Powered, IServerSerializable, IClientSerializable
{
public GUIButton PowerButton { get; private set; }
private GUIScrollBar pumpSpeedSlider;
private GUITickBox powerLight;
private GUITickBox autoControlIndicator;
private List<Pair<Vector2, ParticleEmitter>> pumpOutEmitters = new List<Pair<Vector2, ParticleEmitter>>();
private List<Pair<Vector2, ParticleEmitter>> pumpInEmitters = new List<Pair<Vector2, ParticleEmitter>>();
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "pumpoutemitter":
pumpOutEmitters.Add(new Pair<Vector2, ParticleEmitter>(
subElement.GetAttributeVector2("position", Vector2.Zero),
new ParticleEmitter(subElement)));
break;
case "pumpinemitter":
pumpInEmitters.Add(new Pair<Vector2, ParticleEmitter>(
subElement.GetAttributeVector2("position", Vector2.Zero),
new ParticleEmitter(subElement)));
break;
}
}
if (GuiFrame == null) { return; }
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.85f, 0.65f), GuiFrame.RectTransform, Anchor.Center)
{
RelativeOffset = new Vector2(0, 0.04f)
}, style: null);
// Power button
float powerButtonSize = 1f;
var powerArea = new GUIFrame(new RectTransform(new Vector2(0.3f, 1) * powerButtonSize, paddedFrame.RectTransform, Anchor.CenterLeft), style: null);
var paddedPowerArea = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.8f), powerArea.RectTransform, Anchor.Center), style: "PowerButtonFrame");
var powerLightArea = new GUIFrame(new RectTransform(new Vector2(0.87f, 0.2f), powerArea.RectTransform, Anchor.TopRight), style: null);
powerLight = new GUITickBox(new RectTransform(Vector2.One, powerLightArea.RectTransform, Anchor.Center),
TextManager.Get("PowerLabel"), font: GUI.SubHeadingFont, style: "IndicatorLightPower")
{
CanBeFocused = false
};
powerLight.TextBlock.AutoScaleHorizontal = true;
powerLight.TextBlock.OverrideTextColor(GUI.Style.TextColor);
PowerButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.75f), paddedPowerArea.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0, 0.1f)
}, style: "PowerButton")
{
OnClicked = (button, data) =>
{
targetLevel = null;
IsActive = !IsActive;
if (GameMain.Client != null)
{
correctionTimer = CorrectionDelay;
item.CreateClientEvent(this);
}
powerLight.Selected = IsActive;
return true;
}
};
var rightArea = new GUIFrame(new RectTransform(new Vector2(0.65f, 1), paddedFrame.RectTransform, Anchor.CenterRight), style: null);
autoControlIndicator = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.25f), rightArea.RectTransform, Anchor.TopLeft),
TextManager.Get("PumpAutoControl", fallBackTag: "ReactorAutoControl"), font: GUI.SubHeadingFont, style: "IndicatorLightYellow")
{
CanBeFocused = false
};
autoControlIndicator.TextBlock.AutoScaleHorizontal = true;
autoControlIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);
var sliderArea = new GUIFrame(new RectTransform(new Vector2(1, 0.65f), rightArea.RectTransform, Anchor.BottomLeft), style: null);
var pumpSpeedText = new GUITextBlock(new RectTransform(new Vector2(1, 0.3f), sliderArea.RectTransform, Anchor.TopLeft), "",
textColor: GUI.Style.TextColor, textAlignment: Alignment.CenterLeft, wrap: false, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true
};
string pumpSpeedStr = TextManager.Get("PumpSpeed");
pumpSpeedText.TextGetter = () => { return TextManager.AddPunctuation(':', pumpSpeedStr, (int)flowPercentage + " %"); };
pumpSpeedSlider = new GUIScrollBar(new RectTransform(new Vector2(1, 0.35f), sliderArea.RectTransform, Anchor.Center), barSize: 0.1f, style: "DeviceSlider")
{
Step = 0.05f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
float newValue = barScroll * 200.0f - 100.0f;
if (Math.Abs(newValue - FlowPercentage) < 0.1f) { return false; }
FlowPercentage = newValue;
if (GameMain.Client != null)
{
correctionTimer = CorrectionDelay;
item.CreateClientEvent(this);
}
return true;
}
};
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: GUI.Style.TextColor, textAlignment: Alignment.CenterLeft, wrap: false, font: GUI.SubHeadingFont);
var inLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), textsArea.RectTransform, Anchor.CenterRight), TextManager.Get("PumpIn"),
textColor: GUI.Style.TextColor, textAlignment: Alignment.CenterRight, wrap: false, font: GUI.SubHeadingFont);
GUITextBlock.AutoScaleAndNormalize(outLabel, inLabel);
}
public override void OnItemLoaded()
{
base.OnItemLoaded();
if (pumpSpeedSlider != null)
{
pumpSpeedSlider.BarScroll = (flowPercentage + 100.0f) / 200.0f;
}
}
partial void UpdateProjSpecific(float deltaTime)
{
if (FlowPercentage < 0.0f)
{
foreach (Pair<Vector2, ParticleEmitter> pumpOutEmitter in pumpOutEmitters)
{
//only emit "pump out" particles when underwater
Vector2 particlePos = item.Rect.Location.ToVector2() + pumpOutEmitter.First;
if (item.CurrentHull != null && item.CurrentHull.Surface < particlePos.Y) continue;
pumpOutEmitter.Second.Emit(deltaTime, item.WorldRect.Location.ToVector2() + pumpOutEmitter.First * item.Scale, item.CurrentHull,
velocityMultiplier: MathHelper.Lerp(0.5f, 1.0f, -FlowPercentage / 100.0f));
}
}
else if (FlowPercentage > 0.0f)
{
foreach (Pair<Vector2, ParticleEmitter> pumpInEmitter in pumpInEmitters)
{
pumpInEmitter.Second.Emit(deltaTime, item.WorldRect.Location.ToVector2() + pumpInEmitter.First * item.Scale, item.CurrentHull,
velocityMultiplier: MathHelper.Lerp(0.5f, 1.0f, FlowPercentage / 100.0f));
}
}
}
private float flickerTimer;
private readonly float flickerFrequency = 1;
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
pumpSpeedLockTimer -= deltaTime;
isActiveLockTimer -= deltaTime;
autoControlIndicator.Selected = pumpSpeedLockTimer > 0.0f || isActiveLockTimer > 0.0f;
PowerButton.Enabled = isActiveLockTimer <= 0.0f;
if (HasPower)
{
flickerTimer = 0;
powerLight.Selected = IsActive;
}
else if (IsActive)
{
flickerTimer += deltaTime;
if (flickerTimer > flickerFrequency)
{
flickerTimer = 0;
powerLight.Selected = !powerLight.Selected;
}
}
else
{
flickerTimer = 0;
powerLight.Selected = false;
}
pumpSpeedSlider.Enabled = pumpSpeedLockTimer <= 0.0f && IsActive;
if (!PlayerInput.PrimaryMouseButtonHeld())
{
float pumpSpeedScroll = (FlowPercentage + 100.0f) / 200.0f;
if (Math.Abs(pumpSpeedScroll - pumpSpeedSlider.BarScroll) > 0.01f)
{
pumpSpeedSlider.BarScroll = pumpSpeedScroll;
}
}
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
//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);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{
StartDelayedCorrection(type, msg.ExtractBits(5 + 1), sendingTime);
return;
}
FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
IsActive = msg.ReadBoolean();
}
}
}
@@ -0,0 +1,749 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
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
{
partial class Reactor : Powered, IServerSerializable, IClientSerializable
{
public GUIButton AutoTempSwitch { get; private set; }
public GUIButton PowerButton { get; private set; }
private GUITickBox powerLight;
private GUITickBox autoTempLight;
private const int GraphSize = 25;
private float graphTimer;
private readonly int updateGraphInterval = 500;
private Sprite fissionRateMeter, turbineOutputMeter;
private Sprite meterPointer;
private Sprite sectorSprite;
private Sprite tempMeterFrame, tempMeterBar;
private Sprite tempRangeIndicator;
private Sprite graphLine;
//private GUIFrame graph;
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 Color outputColor = Color.Goldenrod;
private Color loadColor = Color.LightSteelBlue;
public GUIScrollBar FissionRateScrollBar { get; private set; }
public GUIScrollBar TurbineOutputScrollBar { get; private set; }
private readonly float[] outputGraph = new float[GraphSize];
private readonly float[] loadGraph = new float[GraphSize];
private GUITickBox criticalHeatWarning;
private GUITickBox lowTemperatureWarning;
private GUITickBox criticalOutputWarning;
private GUIFrame inventoryContainer;
private readonly Dictionary<string, GUIButton> warningButtons = new Dictionary<string, GUIButton>();
private static readonly string[] warningTexts = new string[]
{
"ReactorWarningLowTemp", "ReactorWarningLowOutput", "ReactorWarningLowFuel", "ReactorWarningMeltdown",
"ReactorWarningOverheating", "ReactorWarningHighOutput", "ReactorWarningFuelOut", "ReactorWarningSCRAM"
};
partial void InitProjSpecific(XElement element)
{
// TODO: need to recreate the gui when the resolution changes
fissionRateMeter = new Sprite(element.GetChildElement("fissionratemeter")?.GetChildElement("sprite"));
turbineOutputMeter = new Sprite(element.GetChildElement("turbineoutputmeter")?.GetChildElement("sprite"));
meterPointer = new Sprite(element.GetChildElement("meterpointer")?.GetChildElement("sprite"));
sectorSprite = new Sprite(element.GetChildElement("sectorsprite")?.GetChildElement("sprite"));
tempMeterFrame = new Sprite(element.GetChildElement("tempmeterframe")?.GetChildElement("sprite"));
tempMeterBar = new Sprite(element.GetChildElement("tempmeterbar")?.GetChildElement("sprite"));
tempRangeIndicator = new Sprite(element.GetChildElement("temprangeindicator")?.GetChildElement("sprite"));
graphLine = new Sprite(element.GetChildElement("graphline")?.GetChildElement("sprite"));
var paddedFrame = new GUILayoutGroup(new RectTransform(
GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center)
{ AbsoluteOffset = GUIStyle.ItemFrameOffset },
isHorizontal: true)
{
RelativeSpacing = 0.012f,
Stretch = true
};
GUILayoutGroup columnLeft = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), paddedFrame.RectTransform))
{
RelativeSpacing = 0.012f,
Stretch = true
};
GUILayoutGroup columnRight = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), paddedFrame.RectTransform))
{
CanBeFocused = true,
RelativeSpacing = 0.012f,
Stretch = true
};
//----------------------------------------------------------
//left column
//----------------------------------------------------------
GUIFrame inventoryWindow = new GUIFrame(new RectTransform(new Vector2(0.1f, 0.75f), GuiFrame.RectTransform, Anchor.TopLeft, Pivot.TopRight)
{
MinSize = new Point(85, 220),
RelativeOffset = new Vector2(-0.02f, 0)
}, style: "ItemUI");
GUILayoutGroup inventoryContent = new GUILayoutGroup(new RectTransform(inventoryWindow.Rect.Size - GUIStyle.ItemFrameMargin, inventoryWindow.RectTransform, Anchor.Center)
{ AbsoluteOffset = GUIStyle.ItemFrameOffset },
childAnchor: Anchor.TopCenter)
{
Stretch = true
};
/*new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), inventoryContent.RectTransform), "",
textAlignment: Alignment.Center, font: GUI.SubHeadingFont, wrap: true);*/
inventoryContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), inventoryContent.RectTransform), style: null);
//----------------------------------------------------------
//mid column
//----------------------------------------------------------
var topLeftArea = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.2f), columnLeft.RectTransform),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true
};
Point maxIndicatorSize = new Point(int.MaxValue, (int)(40 * GUI.Scale));
criticalHeatWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
TextManager.Get("ReactorWarningCriticalTemp"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
{
CanBeFocused = false
};
lowTemperatureWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
TextManager.Get("ReactorWarningCriticalLowTemp"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
{
CanBeFocused = false
};
criticalOutputWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
TextManager.Get("ReactorWarningCriticalOutput"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
{
CanBeFocused = false
};
List<GUITickBox> indicatorLights = new List<GUITickBox>() { criticalHeatWarning, lowTemperatureWarning, criticalOutputWarning };
indicatorLights.ForEach(l => l.TextBlock.OverrideTextColor(GUI.Style.TextColor));
topLeftArea.Recalculate();
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), columnLeft.RectTransform), style: "HorizontalLine");
float relativeYMargin = 0.02f;
Vector2 relativeTextSize = new Vector2(0.9f, 0.2f);
Vector2 sliderSize = new Vector2(1.0f, 0.125f);
Vector2 meterSize = new Vector2(1, 1 - relativeTextSize.Y - relativeYMargin - sliderSize.Y - 0.1f);
var meterArea = new GUIFrame(new RectTransform(new Vector2(1, 0.6f - relativeYMargin * 2), columnLeft.RectTransform), style: null);
var leftArea = new GUIFrame(new RectTransform(new Vector2(0.49f, 1), meterArea.RectTransform), style: null);
var rightArea = new GUIFrame(new RectTransform(new Vector2(0.49f, 1), meterArea.RectTransform, Anchor.TopCenter, Pivot.TopLeft), style: null);
var fissionRateTextBox = new GUITextBlock(new RectTransform(relativeTextSize, leftArea.RectTransform, Anchor.TopCenter),
TextManager.Get("ReactorFissionRate"), textColor: GUI.Style.TextColor, textAlignment: Alignment.Center, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true
};
var fissionMeter = new GUICustomComponent(new RectTransform(meterSize, leftArea.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0.0f, relativeTextSize.Y + relativeYMargin)
},
DrawFissionRateMeter, null)
{
ToolTip = TextManager.Get("ReactorTipFissionRate")
};
var turbineOutputTextBox = new GUITextBlock(new RectTransform(relativeTextSize, rightArea.RectTransform, Anchor.TopCenter),
TextManager.Get("ReactorTurbineOutput"), textColor: GUI.Style.TextColor, textAlignment: Alignment.Center, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true
};
GUITextBlock.AutoScaleAndNormalize(turbineOutputTextBox, fissionRateTextBox);
var turbineMeter = new GUICustomComponent(new RectTransform(meterSize, rightArea.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0.0f, relativeTextSize.Y + relativeYMargin)
},
DrawTurbineOutputMeter, null)
{
ToolTip = TextManager.Get("ReactorTipTurbineOutput")
};
FissionRateScrollBar = new GUIScrollBar(new RectTransform(sliderSize, leftArea.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0, fissionMeter.RectTransform.RelativeOffset.Y + meterSize.Y)
},
style: "DeviceSlider", barSize: 0.1f)
{
Enabled = false,
OnMoved = (GUIScrollBar bar, float scrollAmount) =>
{
LastUser = Character.Controlled;
unsentChanges = true;
targetFissionRate = scrollAmount * 100.0f;
return false;
}
};
TurbineOutputScrollBar = new GUIScrollBar(new RectTransform(sliderSize, rightArea.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0, turbineMeter.RectTransform.RelativeOffset.Y + meterSize.Y)
},
style: "DeviceSlider", barSize: 0.1f, isHorizontal: true)
{
Enabled = false,
OnMoved = (GUIScrollBar bar, float scrollAmount) =>
{
LastUser = Character.Controlled;
unsentChanges = true;
targetTurbineOutput = scrollAmount * 100.0f;
return false;
}
};
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.2f), columnLeft.RectTransform))
{
Stretch = true,
RelativeSpacing = 0.02f
};
var upperButtons = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.5f), buttonArea.RectTransform), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.01f
};
var lowerButtons = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.5f), buttonArea.RectTransform), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.01f
};
int buttonCount = warningTexts.Length;
for (int i = 0; i < buttonCount; i++)
{
string text = warningTexts[i];
var b = new GUIButton(new RectTransform(Vector2.One, (i < 4) ? upperButtons.RectTransform : lowerButtons.RectTransform),
TextManager.Get(text), style: "IndicatorButton")
{
Font = GUI.SubHeadingFont,
CanBeFocused = false
};
warningButtons.Add(text, b);
}
upperButtons.Recalculate();
lowerButtons.Recalculate();
//only wrap texts that consist of multiple words and are way too big to fit otherwise
warningButtons.Values.ForEach(b => b.TextBlock.Wrap = b.Text.Contains(' ') && b.TextBlock.TextSize.X > b.TextBlock.Rect.Width * 1.5f);
GUITextBlock.AutoScaleAndNormalize(warningButtons.Values.Select(b => b.TextBlock));
//----------------------------------------------------------
//right column
//----------------------------------------------------------
// Auto temp
var topRightArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), columnRight.RectTransform),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.02f
};
topRightArea.RectTransform.MinSize = new Point(0, topLeftArea.Rect.Height);
topRightArea.RectTransform.MaxSize = new Point(int.MaxValue, topLeftArea.Rect.Height);
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),
style: "SwitchVertical")
{
Enabled = false,
Selected = AutoTemp,
OnClicked = (button, data) =>
{
AutoTemp = !AutoTemp;
LastUser = Character.Controlled;
unsentChanges = true;
return true;
}
};
AutoTempSwitch.RectTransform.MaxSize = new Point((int)(AutoTempSwitch.Rect.Height * 0.4f), int.MaxValue);
autoTempLight = new GUITickBox(new RectTransform(new Vector2(0.4f, 1.0f), topRightArea.RectTransform),
TextManager.Get("ReactorAutoTemp"), font: GUI.SubHeadingFont, style: "IndicatorLightYellow")
{
ToolTip = TextManager.Get("ReactorTipAutoTemp"),
CanBeFocused = false,
Selected = AutoTemp
};
autoTempLight.RectTransform.MaxSize = new Point(int.MaxValue, criticalHeatWarning.Rect.Height);
autoTempLight.TextBlock.OverrideTextColor(GUI.Style.TextColor);
new GUIFrame(new RectTransform(new Vector2(0.01f, 1.0f), topRightArea.RectTransform), style: "VerticalLine");
// Power button
var powerArea = new GUIFrame(new RectTransform(new Vector2(0.4f, 1.0f), topRightArea.RectTransform), style: null);
var paddedPowerArea = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), powerArea.RectTransform, Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: "PowerButtonFrame");
powerLight = new GUITickBox(new RectTransform(new Vector2(0.87f, 0.3f), paddedPowerArea.RectTransform, Anchor.TopCenter, Pivot.Center),
TextManager.Get("PowerLabel"), font: GUI.SubHeadingFont, style: "IndicatorLightPower")
{
CanBeFocused = false,
Selected = _powerOn
};
powerLight.TextBlock.Padding = new Vector4(5.0f, 0.0f, 0.0f, 0.0f);
powerLight.TextBlock.AutoScaleHorizontal = true;
powerLight.TextBlock.OverrideTextColor(GUI.Style.TextColor);
PowerButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.75f), paddedPowerArea.RectTransform, Anchor.BottomCenter)
{
RelativeOffset = new Vector2(0, 0.1f)
}, style: "PowerButton")
{
OnClicked = (button, data) =>
{
PowerOn = !PowerOn;
LastUser = Character.Controlled;
unsentChanges = true;
return true;
}
};
topRightArea.Recalculate();
autoTempLight.TextBlock.Wrap = true;
indicatorLights.Add(autoTempLight);
GUITextBlock.AutoScaleAndNormalize(indicatorLights.Select(l => l.TextBlock));
// right bottom (graph area) -----------------------
new GUIFrame(new RectTransform(new Vector2(0.95f, 0.01f), columnRight.RectTransform), style: "HorizontalLine");
var bottomRightArea = new GUILayoutGroup(new RectTransform(Vector2.One, columnRight.RectTransform), isHorizontal: true)
{
Stretch = true,
CanBeFocused = true,
RelativeSpacing = 0.02f
};
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 graphArea = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), bottomRightArea.RectTransform))
{
Stretch = true,
RelativeSpacing = 0.02f
};
relativeTextSize = new Vector2(1.0f, 0.15f);
var loadText = new GUITextBlock(new RectTransform(relativeTextSize, graphArea.RectTransform),
"Load", textColor: loadColor, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
{
ToolTip = TextManager.Get("ReactorTipLoad")
};
string loadStr = TextManager.Get("ReactorLoad");
string kW = TextManager.Get("kilowatt");
loadText.TextGetter += () => $"{loadStr.Replace("[kw]", ((int)load).ToString())} {kW}";
var graph = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), graphArea.RectTransform), style: "InnerFrameRed");
new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.98f), graph.RectTransform, Anchor.Center), DrawGraph, null);
var outputText = new GUITextBlock(new RectTransform(relativeTextSize, graphArea.RectTransform),
"Output", textColor: outputColor, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
{
ToolTip = TextManager.Get("ReactorTipPower")
};
string outputStr = TextManager.Get("ReactorOutput");
outputText.TextGetter += () => $"{outputStr.Replace("[kw]", ((int)-currPowerConsumption).ToString())} {kW}";
}
public override void OnItemLoaded()
{
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();
}*/
}
}
private void DrawTempMeter(SpriteBatch spriteBatch, GUICustomComponent container)
{
Vector2 meterPos = new Vector2(container.Rect.X, container.Rect.Y);
Vector2 meterScale = new Vector2(container.Rect.Width / (float)tempMeterFrame.SourceRect.Width, container.Rect.Height / (float)tempMeterFrame.SourceRect.Height);
tempMeterFrame.Draw(spriteBatch, meterPos, Color.White, tempMeterFrame.Origin, 0.0f, scale: meterScale);
float tempFill = temperature / 100.0f;
float meterBarScale = container.Rect.Width / (float)tempMeterBar.SourceRect.Width;
Vector2 meterBarPos = new Vector2(container.Center.X, container.Rect.Bottom - tempMeterBar.size.Y * meterBarScale - (int)(5 * GUI.yScale));
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);
tempMeterBar.Draw(spriteBatch, meterBarPos, color: color, scale: meterBarScale);
int spacing = 2;
meterBarPos.Y -= tempMeterBar.size.Y * meterBarScale + spacing;
}
if (temperature > optimalTemperature.Y)
{
GUI.DrawRectangle(spriteBatch,
meterPos,
new Vector2(container.Rect.Width, (container.Rect.Bottom - container.Rect.Height * optimalTemperature.Y / 100.0f) - container.Rect.Y),
warningColor * (float)Math.Sin(Timing.TotalTime * 5.0f) * 0.7f, isFilled: true);
}
if (temperature < optimalTemperature.X)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(meterPos.X, container.Rect.Bottom - container.Rect.Height * optimalTemperature.X / 100.0f),
new Vector2(container.Rect.Width, container.Rect.Bottom - (container.Rect.Bottom - container.Rect.Height * optimalTemperature.X / 100.0f)),
warningColor * (float)Math.Sin(Timing.TotalTime * 5.0f) * 0.7f, isFilled: true);
}
float tempRangeIndicatorScale = container.Rect.Width / (float)tempRangeIndicator.SourceRect.Width;
tempRangeIndicator.Draw(spriteBatch, new Vector2(container.Center.X, container.Rect.Bottom - container.Rect.Height * optimalTemperature.X / 100.0f), Color.White, tempRangeIndicator.Origin, 0, scale: tempRangeIndicatorScale);
tempRangeIndicator.Draw(spriteBatch, new Vector2(container.Center.X, container.Rect.Bottom - container.Rect.Height * optimalTemperature.Y / 100.0f), Color.White, tempRangeIndicator.Origin, 0, scale: tempRangeIndicatorScale);
}
private void DrawGraph(SpriteBatch spriteBatch, GUICustomComponent container)
{
if (item.Removed) { return; }
float maxLoad = loadGraph.Max();
float xOffset = graphTimer / updateGraphInterval;
Rectangle graphRect = new Rectangle(container.Rect.X, container.Rect.Y, container.Rect.Width, container.Rect.Height - (int)(5 * GUI.yScale));
DrawGraph(outputGraph, spriteBatch, graphRect, Math.Max(10000.0f, maxLoad), xOffset, outputColor);
DrawGraph(loadGraph, spriteBatch, graphRect, Math.Max(10000.0f, maxLoad), xOffset, loadColor);
}
private void UpdateGraph(float deltaTime)
{
graphTimer += deltaTime * 1000.0f;
if (graphTimer > updateGraphInterval)
{
UpdateGraph(outputGraph, -currPowerConsumption);
UpdateGraph(loadGraph, load);
graphTimer = 0.0f;
}
if (autoTemp)
{
FissionRateScrollBar.BarScroll = FissionRate / 100.0f;
TurbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
}
}
private void DrawFissionRateMeter(SpriteBatch spriteBatch, GUICustomComponent container)
{
if (item.Removed) { return; }
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = container.Rect;
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
//make the pointer jitter a bit if it's at the upper limit of the fission rate
float jitter = 0.0f;
if (FissionRate > allowedFissionRate.Y - 5.0f)
{
float jitterAmount = Math.Min(targetFissionRate - allowedFissionRate.Y, 10.0f);
float t = graphTimer / updateGraphInterval;
jitter = (PerlinNoise.GetPerlin(t * 0.5f, t * 0.1f) - 0.5f) * jitterAmount;
}
DrawMeter(spriteBatch, container.Rect,
fissionRateMeter, FissionRate + jitter, new Vector2(0.0f, 100.0f), optimalFissionRate, allowedFissionRate);
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred);
}
private void DrawTurbineOutputMeter(SpriteBatch spriteBatch, GUICustomComponent container)
{
if (item.Removed) { return; }
DrawMeter(spriteBatch, container.Rect,
turbineOutputMeter, TurbineOutput, new Vector2(0.0f, 100.0f), optimalTurbineOutput, allowedTurbineOutput);
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
IsActive = true;
bool lightOn = Timing.TotalTime % 0.5f < 0.25f && PowerOn;
criticalHeatWarning.Selected = temperature > allowedTemperature.Y && lightOn;
lowTemperatureWarning.Selected = temperature < allowedTemperature.X && lightOn;
criticalOutputWarning.Selected = -currPowerConsumption > load * 1.5f && lightOn;
warningButtons["ReactorWarningOverheating"].Selected = temperature > optimalTemperature.Y && lightOn;
warningButtons["ReactorWarningHighOutput"].Selected = -currPowerConsumption > load * 1.1f && lightOn;
warningButtons["ReactorWarningLowTemp"].Selected = temperature < optimalTemperature.X && lightOn;
warningButtons["ReactorWarningLowOutput"].Selected = -currPowerConsumption < load * 0.9f && lightOn;
warningButtons["ReactorWarningFuelOut"].Selected = prevAvailableFuel < fissionRate * 0.01f && lightOn;
warningButtons["ReactorWarningLowFuel"].Selected = prevAvailableFuel < fissionRate && lightOn;
warningButtons["ReactorWarningMeltdown"].Selected = meltDownTimer > MeltdownDelay * 0.5f || item.Condition == 0.0f && lightOn;
warningButtons["ReactorWarningSCRAM"].Selected = temperature > 0.1f && !PowerOn;
if ((FissionRateScrollBar.Rect.Contains(PlayerInput.MousePosition) || FissionRateScrollBar.Children.Contains(GUIScrollBar.DraggingBar) ||
TurbineOutputScrollBar.Rect.Contains(PlayerInput.MousePosition) || TurbineOutputScrollBar.Children.Contains(GUIScrollBar.DraggingBar)) &&
!PlayerInput.KeyDown(InputType.Deselect) && !PlayerInput.KeyHit(InputType.Deselect))
{
Character.DisableControls = true;
}
if (!PowerOn)
{
FissionRateScrollBar.BarScroll = FissionRate / 100.0f;
TurbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
}
else if (!autoTemp && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
{
Vector2 input = Vector2.Zero;
float rate = 50.0f; //percentage per second
if (PlayerInput.KeyDown(InputType.Left)) input.X += -1.0f;
if (PlayerInput.KeyDown(InputType.Right)) input.X += 1.0f;
if (PlayerInput.KeyDown(InputType.Up)) input.Y += 1.0f;
if (PlayerInput.KeyDown(InputType.Down)) input.Y += -1.0f;
if (PlayerInput.KeyDown(InputType.Run))
rate = 200.0f;
else if (PlayerInput.KeyDown(InputType.Crouch))
rate = 20.0f;
rate *= deltaTime;
input.X *= rate;
input.Y *= rate;
if (input.LengthSquared() > 0)
{
LastUser = Character.Controlled;
unsentChanges = true;
if (input.X != 0.0f && GUIScrollBar.DraggingBar != FissionRateScrollBar)
{
targetFissionRate = MathHelper.Clamp(targetFissionRate + input.X, 0.0f, 100.0f);
FissionRateScrollBar.BarScroll += input.X / 100.0f;
}
if (input.Y != 0.0f && GUIScrollBar.DraggingBar != TurbineOutputScrollBar)
{
targetTurbineOutput = MathHelper.Clamp(targetTurbineOutput + input.Y, 0.0f, 100.0f);
TurbineOutputScrollBar.BarScroll += input.Y / 100.0f;
}
}
}
}
private void DrawMeter(SpriteBatch spriteBatch, Rectangle rect, Sprite meterSprite, float value, Vector2 range, Vector2 optimalRange, Vector2 allowedRange)
{
float scale = Math.Min(rect.Width / meterSprite.size.X, rect.Height / meterSprite.size.Y);
Vector2 pos = new Vector2(rect.Center.X, rect.Y + meterSprite.Origin.Y * scale);
Vector2 optimalRangeNormalized = new Vector2(
MathHelper.Clamp((optimalRange.X - range.X) / (range.Y - range.X), 0.0f, 0.95f),
MathHelper.Clamp((optimalRange.Y - range.X) / (range.Y - range.X), 0.0f, 1.0f));
Vector2 allowedRangeNormalized = new Vector2(
MathHelper.Clamp((allowedRange.X - range.X) / (range.Y - range.X), 0.0f, 0.95f),
MathHelper.Clamp((allowedRange.Y - range.X) / (range.Y - range.X), 0.0f, 1.0f));
Vector2 sectorRad = new Vector2(-1.57f, 1.57f);
Vector2 optimalSectorRad = new Vector2(
MathHelper.Lerp(sectorRad.X, sectorRad.Y, optimalRangeNormalized.X),
MathHelper.Lerp(sectorRad.X, sectorRad.Y, optimalRangeNormalized.Y));
Vector2 allowedSectorRad = new Vector2(
MathHelper.Lerp(sectorRad.X, sectorRad.Y, allowedRangeNormalized.X),
MathHelper.Lerp(sectorRad.X, sectorRad.Y, allowedRangeNormalized.Y));
if (optimalRangeNormalized.X == optimalRangeNormalized.Y)
{
sectorSprite.Draw(spriteBatch, pos, GUI.Style.Red, MathHelper.PiOver2, scale);
}
else
{
spriteBatch.End();
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0, 0, GameMain.GraphicsWidth, (int)(pos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale) - 3);
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
float scaleMultiplier = 0.95f;
sectorSprite.Draw(spriteBatch, pos, optimalRangeColor, MathHelper.PiOver2 + (allowedSectorRad.X + allowedSectorRad.Y) / 2.0f, scale * scaleMultiplier);
sectorSprite.Draw(spriteBatch, pos, offRangeColor, optimalSectorRad.X, scale * scaleMultiplier);
sectorSprite.Draw(spriteBatch, pos, warningColor, allowedSectorRad.X, scale * scaleMultiplier);
sectorSprite.Draw(spriteBatch, pos, offRangeColor, MathHelper.Pi + optimalSectorRad.Y, scale * scaleMultiplier);
sectorSprite.Draw(spriteBatch, pos, warningColor, MathHelper.Pi + allowedSectorRad.Y, scale * scaleMultiplier);
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred);
}
meterSprite.Draw(spriteBatch, pos, 0, scale);
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, pos - offset, valueRad, scale);
}
static void UpdateGraph<T>(IList<T> graph, T newValue)
{
for (int i = graph.Count - 1; i > 0; i--)
{
graph[i] = graph[i - 1];
}
graph[0] = newValue;
}
private void DrawGraph(IList<float> graph, SpriteBatch spriteBatch, Rectangle rect, float maxVal, float xOffset, Color color)
{
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = rect;
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
float lineWidth = (float)rect.Width / (float)(graph.Count - 2);
float yScale = (float)rect.Height / maxVal;
Vector2 prevPoint = new Vector2(rect.Right, rect.Bottom - (graph[1] + (graph[0] - graph[1]) * xOffset) * yScale);
float currX = rect.Right - ((xOffset - 1.0f) * lineWidth);
for (int i = 1; i < graph.Count - 1; i++)
{
currX -= lineWidth;
Vector2 newPoint = new Vector2(currX, rect.Bottom - graph[i] * yScale);
if (graphLine?.Texture == null)
{
GUI.DrawLine(spriteBatch, prevPoint, newPoint - new Vector2(1.0f, 0), color);
}
else
{
Vector2 dir = Vector2.Normalize(newPoint - prevPoint);
GUI.DrawLine(spriteBatch, graphLine.Texture, prevPoint - dir, newPoint + dir, color, 0, 5);
}
prevPoint = newPoint;
}
Vector2 lastPoint = new Vector2(rect.X,
rect.Bottom - (graph[graph.Count - 1] + (graph[graph.Count - 2] - graph[graph.Count - 1]) * xOffset) * yScale);
if (graphLine?.Texture == null)
{
GUI.DrawLine(spriteBatch, prevPoint, lastPoint, color);
}
else
{
GUI.DrawLine(spriteBatch, graphLine.Texture, prevPoint, lastPoint + (lastPoint - prevPoint), color, 0, 5);
}
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred);
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
graphLine?.Remove();
fissionRateMeter?.Remove();
turbineOutputMeter?.Remove();
meterPointer?.Remove();
sectorSprite?.Remove();
tempMeterFrame?.Remove();
tempMeterBar?.Remove();
tempRangeIndicator?.Remove();
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.Write(autoTemp);
msg.Write(PowerOn);
msg.WriteRangedSingle(targetFissionRate, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(targetTurbineOutput, 0.0f, 100.0f, 8);
correctionTimer = CorrectionDelay;
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{
StartDelayedCorrection(type, msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8), sendingTime);
return;
}
AutoTemp = msg.ReadBoolean();
PowerOn = msg.ReadBoolean();
Temperature = msg.ReadRangedSingle(0.0f, 100.0f, 8);
targetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
targetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8);
degreeOfSuccess = msg.ReadRangedSingle(0.0f, 1.0f, 8);
FissionRateScrollBar.BarScroll = targetFissionRate / 100.0f;
TurbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f;
IsActive = true;
}
private void UpdateUIElementStates()
{
if (powerLight != null)
{
powerLight.Selected = _powerOn;
}
if (AutoTempSwitch != null)
{
AutoTempSwitch.Selected = autoTemp;
AutoTempSwitch.Enabled = _powerOn;
}
if (autoTempLight != null)
{
autoTempLight.Selected = autoTemp && _powerOn;
}
if (FissionRateScrollBar != null)
{
FissionRateScrollBar.Enabled = _powerOn && !autoTemp;
}
if (TurbineOutputScrollBar != null)
{
TurbineOutputScrollBar.Enabled = _powerOn && !autoTemp;
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,924 @@
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;
namespace Barotrauma.Items.Components
{
partial class Steering : Powered, IServerSerializable, IClientSerializable
{
private GUIButton steeringModeSwitch;
private GUITickBox autopilotIndicator, manualPilotIndicator;
enum Destination
{
MaintainPos,
LevelEnd,
LevelStart
};
private GUITickBox maintainPosTickBox, levelEndTickBox, levelStartTickBox;
private GUIComponent statusContainer, dockingContainer, controlContainer;
private bool dockingNetworkMessagePending;
private GUIButton dockingButton;
private string dockText, undockText;
private GUIComponent steerArea;
private GUITextBlock pressureWarningText;
private GUITextBlock tipContainer;
private string noPowerTip, autoPilotMaintainPosTip, autoPilotLevelStartTip, autoPilotLevelEndTip;
private Sprite maintainPosIndicator, maintainPosOriginIndicator;
private Sprite steeringIndicator;
private List<DockingPort> connectedPorts = new List<DockingPort>();
private float checkConnectedPortsTimer;
private const float CheckConnectedPortsInterval = 1.0f;
private Vector2 keyboardInput = Vector2.Zero;
private float inputCumulation;
private bool? swapDestinationOrder;
private bool levelStartSelected;
public bool LevelStartSelected
{
get { return levelStartTickBox.Selected; }
set { levelStartTickBox.Selected = value; }
}
private bool levelEndSelected;
public bool LevelEndSelected
{
get { return levelEndTickBox.Selected; }
set { levelEndTickBox.Selected = value; }
}
private bool maintainPos;
public bool MaintainPos
{
get { return maintainPosTickBox.Selected; }
set { maintainPosTickBox.Selected = value; }
}
private float steerRadius;
public float? SteerRadius
{
get
{
return steerRadius;
}
set
{
steerRadius = value ?? (steerArea.Rect.Width / 2);
}
}
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "steeringindicator":
steeringIndicator = new Sprite(subElement);
break;
case "maintainposindicator":
maintainPosIndicator = new Sprite(subElement);
break;
case "maintainposoriginindicator":
maintainPosOriginIndicator = new Sprite(subElement);
break;
}
}
CreateGUI();
GameMain.Instance.OnResolutionChanged += RecreateGUI;
}
private void CreateGUI()
{
controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterLeft), "ItemUI");
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
{
AbsoluteOffset = GUIStyle.ItemFrameOffset
}, style: null);
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")
{
Selected = autoPilot,
Enabled = true,
OnClicked = (button, data) =>
{
button.Selected = !button.Selected;
AutoPilot = button.Selected;
if (GameMain.Client != null)
{
unsentChanges = true;
user = Character.Controlled;
}
return true;
}
};
var steeringModeRightSide = new GUIFrame(new RectTransform(new Vector2(1.0f - steeringModeSwitch.RectTransform.RelativeSize.X, 0.8f), steeringModeArea.RectTransform, Anchor.CenterLeft)
{
RelativeOffset = new Vector2(steeringModeSwitch.RectTransform.RelativeSize.X, 0)
}, style: null);
manualPilotIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), steeringModeRightSide.RectTransform, Anchor.TopLeft),
TextManager.Get("SteeringManual"), font: GUI.SubHeadingFont, style: "IndicatorLightRedSmall")
{
Selected = !autoPilot,
Enabled = false
};
autopilotIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), steeringModeRightSide.RectTransform, Anchor.BottomLeft),
TextManager.Get("SteeringAutoPilot"), font: GUI.SubHeadingFont, style: "IndicatorLightRedSmall")
{
Selected = autoPilot,
Enabled = false
};
manualPilotIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);
autopilotIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);
GUITextBlock.AutoScaleAndNormalize(manualPilotIndicator.TextBlock, autopilotIndicator.TextBlock);
var autoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.62f), paddedControlContainer.RectTransform, Anchor.BottomCenter), "OutlineFrame");
var paddedAutoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.92f, 0.88f), autoPilotControls.RectTransform, Anchor.Center), style: null);
maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.TopCenter),
TextManager.Get("SteeringMaintainPos"), font: GUI.SmallFont, style: "GUIRadioButton")
{
Enabled = autoPilot,
Selected = maintainPos,
OnSelected = tickBox =>
{
if (maintainPos != tickBox.Selected)
{
unsentChanges = true;
user = Character.Controlled;
maintainPos = tickBox.Selected;
if (maintainPos)
{
if (controlledSub == null)
{
posToMaintain = null;
}
else
{
posToMaintain = controlledSub.WorldPosition;
}
}
else if (!LevelEndSelected && !LevelStartSelected)
{
AutoPilot = false;
}
if (!maintainPos)
{
posToMaintain = null;
}
}
return true;
}
};
int textLimit = (int)(MathHelper.Clamp(25 * GUI.xScale, 15, 35));
levelStartTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.Center),
GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, textLimit),
font: GUI.SmallFont, style: "GUIRadioButton")
{
Enabled = autoPilot,
Selected = levelStartSelected,
OnSelected = tickBox =>
{
if (levelStartSelected != tickBox.Selected)
{
unsentChanges = true;
user = Character.Controlled;
levelStartSelected = tickBox.Selected;
levelEndSelected = !levelStartSelected;
if (levelStartSelected)
{
UpdatePath();
}
else if (!MaintainPos && !LevelEndSelected)
{
AutoPilot = false;
}
}
return true;
}
};
levelEndTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.BottomCenter),
GameMain.GameSession?.EndLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, textLimit),
font: GUI.SmallFont, style: "GUIRadioButton")
{
Enabled = autoPilot,
Selected = levelEndSelected,
OnSelected = tickBox =>
{
if (levelEndSelected != tickBox.Selected)
{
unsentChanges = true;
user = Character.Controlled;
levelEndSelected = tickBox.Selected;
levelStartSelected = !levelEndSelected;
if (levelEndSelected)
{
UpdatePath();
}
else if (!MaintainPos && !LevelStartSelected)
{
AutoPilot = false;
}
}
return true;
}
};
maintainPosTickBox.RectTransform.IsFixedSize = levelStartTickBox.RectTransform.IsFixedSize = levelEndTickBox.RectTransform.IsFixedSize = false;
maintainPosTickBox.RectTransform.MaxSize = levelStartTickBox.RectTransform.MaxSize = levelEndTickBox.RectTransform.MaxSize =
new Point(int.MaxValue, paddedAutoPilotControls.Rect.Height / 3);
maintainPosTickBox.RectTransform.MinSize = levelStartTickBox.RectTransform.MinSize = levelEndTickBox.RectTransform.MinSize =
Point.Zero;
GUITextBlock.AutoScaleAndNormalize(scaleHorizontal: false, scaleVertical: true, maintainPosTickBox.TextBlock, levelStartTickBox.TextBlock, levelEndTickBox.TextBlock);
GUIRadioButtonGroup destinations = new GUIRadioButtonGroup();
destinations.AddRadioButton((int)Destination.MaintainPos, maintainPosTickBox);
destinations.AddRadioButton((int)Destination.LevelStart, levelStartTickBox);
destinations.AddRadioButton((int)Destination.LevelEnd, levelEndTickBox);
destinations.Selected = (int)(maintainPos ? Destination.MaintainPos :
levelStartSelected ? Destination.LevelStart : Destination.LevelEnd);
// Status ->
statusContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomLeft)
{
RelativeOffset = Sonar.controlBoxOffset
}, "ItemUI");
var paddedStatusContainer = new GUIFrame(new RectTransform(statusContainer.Rect.Size - GUIStyle.ItemFrameMargin, statusContainer.RectTransform, Anchor.Center, isFixedSize: false)
{
AbsoluteOffset = GUIStyle.ItemFrameOffset
}, style: null);
var elements = GUI.CreateElements(3, new Vector2(1f, 0.333f), paddedStatusContainer.RectTransform, rt => new GUIFrame(rt, style: null), Anchor.TopCenter, relativeSpacing: 0.01f);
List<GUIComponent> leftElements = new List<GUIComponent>(), centerElements = new List<GUIComponent>(), rightElements = new List<GUIComponent>();
for (int i = 0; i < elements.Count; i++)
{
var e = elements[i];
var group = new GUILayoutGroup(new RectTransform(Vector2.One, e.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
RelativeSpacing = 0.01f,
Stretch = true
};
var left = new GUIFrame(new RectTransform(new Vector2(0.45f, 1), group.RectTransform), style: null);
var center = new GUIFrame(new RectTransform(new Vector2(0.15f, 1), group.RectTransform), style: null);
var right = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.8f), group.RectTransform), style: null);
leftElements.Add(left);
centerElements.Add(center);
rightElements.Add(right);
string leftText = string.Empty, centerText = string.Empty;
GUITextBlock.TextGetterHandler rightTextGetter = null;
switch (i)
{
case 0:
leftText = TextManager.Get("DescentVelocity");
centerText = $"({TextManager.Get("KilometersPerHour")})";
rightTextGetter = () =>
{
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.Y * Physics.DisplayToRealWorldRatio) * 3.6f;
return ((int)(-realWorldVel)).ToString();
};
break;
case 1:
leftText = TextManager.Get("Velocity");
centerText = $"({TextManager.Get("KilometersPerHour")})";
rightTextGetter = () =>
{
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.X * Physics.DisplayToRealWorldRatio) * 3.6f;
return ((int)realWorldVel).ToString();
};
break;
case 2:
leftText = TextManager.Get("Depth");
centerText = $"({TextManager.Get("Meter")})";
rightTextGetter = () =>
{
Vector2 pos = controlledSub == null ? Vector2.Zero : controlledSub.Position;
float realWorldDepth = Level.Loaded == null ? 0.0f : Math.Abs(pos.Y - Level.Loaded.Size.Y) * Physics.DisplayToRealWorldRatio;
return ((int)realWorldDepth).ToString();
};
break;
}
new GUITextBlock(new RectTransform(Vector2.One, left.RectTransform), leftText, font: GUI.SubHeadingFont, wrap: true, textAlignment: Alignment.CenterRight);
new GUITextBlock(new RectTransform(Vector2.One, center.RectTransform), centerText, font: GUI.Font, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
var digitalFrame = new GUIFrame(new RectTransform(Vector2.One, right.RectTransform), style: "DigitalFrameDark");
new GUITextBlock(new RectTransform(Vector2.One * 0.85f, digitalFrame.RectTransform, Anchor.Center), "12345", GUI.Style.TextColorDark, GUI.DigitalFont, Alignment.CenterRight)
{
TextGetter = rightTextGetter
};
}
GUITextBlock.AutoScaleAndNormalize(leftElements.SelectMany(e => e.GetAllChildren<GUITextBlock>()));
GUITextBlock.AutoScaleAndNormalize(centerElements.SelectMany(e => e.GetAllChildren<GUITextBlock>()));
GUITextBlock.AutoScaleAndNormalize(rightElements.SelectMany(e => e.GetAllChildren<GUITextBlock>()));
//docking interface ----------------------------------------------------
float dockingButtonSize = 1.1f;
float elementScale = 0.6f;
dockingContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.Smallest)
{
RelativeOffset = new Vector2(Sonar.controlBoxOffset.X + 0.05f, Sonar.controlBoxOffset.Y)
}, style: null);
dockText = TextManager.Get("label.navterminaldock", fallBackTag: "captain.dock");
undockText = TextManager.Get("label.navterminalundock", fallBackTag: "captain.undock");
dockingButton = new GUIButton(new RectTransform(new Vector2(elementScale), dockingContainer.RectTransform, Anchor.Center), dockText, style: "PowerButton")
{
OnClicked = (btn, userdata) =>
{
if (GameMain.Client == null)
{
item.SendSignal(0, "1", "toggle_docking", sender: null);
}
else
{
dockingNetworkMessagePending = true;
item.CreateClientEvent(this);
}
return true;
}
};
dockingButton.Font = GUI.SubHeadingFont;
dockingButton.TextBlock.RectTransform.MaxSize = new Point((int)(dockingButton.Rect.Width * 0.7f), int.MaxValue);
dockingButton.TextBlock.AutoScaleHorizontal = true;
var style = GUI.Style.GetComponentStyle("DockingButtonUp");
Sprite buttonSprite = style.Sprites.FirstOrDefault().Value.FirstOrDefault()?.Sprite;
Point buttonSize = buttonSprite != null ? buttonSprite.size.ToPoint() : new Point(149, 52);
Point horizontalButtonSize = buttonSize.Multiply(elementScale * GUI.Scale * dockingButtonSize);
Point verticalButtonSize = horizontalButtonSize.Flip();
var leftButton = new GUIButton(new RectTransform(verticalButtonSize, dockingContainer.RectTransform, Anchor.CenterLeft), "", style: "DockingButtonLeft")
{
OnClicked = NudgeButtonClicked,
UserData = -Vector2.UnitX
};
var rightButton = new GUIButton(new RectTransform(verticalButtonSize, dockingContainer.RectTransform, Anchor.CenterRight), "", style: "DockingButtonRight")
{
OnClicked = NudgeButtonClicked,
UserData = Vector2.UnitX
};
var upButton = new GUIButton(new RectTransform(horizontalButtonSize, dockingContainer.RectTransform, Anchor.TopCenter), "", style: "DockingButtonUp")
{
OnClicked = NudgeButtonClicked,
UserData = Vector2.UnitY
};
var downButton = new GUIButton(new RectTransform(horizontalButtonSize, dockingContainer.RectTransform, Anchor.BottomCenter), "", style: "DockingButtonDown")
{
OnClicked = NudgeButtonClicked,
UserData = -Vector2.UnitY
};
// Sonar area
steerArea = new GUICustomComponent(new RectTransform(Vector2.One * GUI.RelativeHorizontalAspectRatio * Sonar.sonarAreaSize, GuiFrame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest),
(spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null);
steerRadius = steerArea.Rect.Width / 2;
pressureWarningText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), steerArea.RectTransform, Anchor.Center, Pivot.TopCenter),
TextManager.Get("SteeringDepthWarning"), Color.Red, GUI.LargeFont, Alignment.Center)
{
Visible = false
};
// Tooltip/helper text
tipContainer = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.1f), steerArea.RectTransform, Anchor.BottomCenter, Pivot.TopCenter)
, "", font: GUI.Font, wrap: true, style: "GUIToolTip", textAlignment: Alignment.Center)
{
AutoScaleHorizontal = true
};
noPowerTip = TextManager.Get("SteeringNoPowerTip");
autoPilotMaintainPosTip = TextManager.Get("SteeringAutoPilotMaintainPosTip");
autoPilotLevelStartTip = TextManager.GetWithVariable("SteeringAutoPilotLocationTip", "[locationname]",
GameMain.GameSession?.StartLocation == null ? "Start" : GameMain.GameSession.StartLocation.Name);
autoPilotLevelEndTip = TextManager.GetWithVariable("SteeringAutoPilotLocationTip", "[locationname]",
GameMain.GameSession?.EndLocation == null ? "End" : GameMain.GameSession.EndLocation.Name);
}
private void RecreateGUI()
{
GuiFrame.ClearChildren();
CreateGUI();
UpdateGUIElements();
}
/// <summary>
/// Makes the sonar view CustomComponent render the steering HUD, preventing it from being drawn behing the sonar
/// </summary>
public void AttachToSonarHUD(GUICustomComponent sonarView)
{
steerArea.Visible = false;
sonarView.OnDraw += (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); };
}
public void DrawHUD(SpriteBatch spriteBatch, Rectangle rect)
{
int width = rect.Width, height = rect.Height;
int x = rect.X;
int y = rect.Y;
if (Voltage < MinVoltage) { return; }
Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40);
Vector2 displaySubPos = (-sonar.DisplayOffset * sonar.Zoom) / sonar.Range * sonar.DisplayRadius * sonar.Zoom;
displaySubPos.Y = -displaySubPos.Y;
displaySubPos = displaySubPos.ClampLength(velRect.Width / 2);
displaySubPos = steerArea.Rect.Center.ToVector2() + displaySubPos;
if (!AutoPilot)
{
Vector2 unitSteeringInput = steeringInput / 100.0f;
//map input from rectangle to circle
Vector2 steeringInputPos = new Vector2(
steeringInput.X * (float)Math.Sqrt(1.0f - 0.5f * unitSteeringInput.Y * unitSteeringInput.Y),
-steeringInput.Y * (float)Math.Sqrt(1.0f - 0.5f * unitSteeringInput.X * unitSteeringInput.X));
steeringInputPos += displaySubPos;
if (steeringIndicator != null)
{
Vector2 dir = steeringInputPos - displaySubPos;
float angle = (float)Math.Atan2(dir.Y, dir.X);
steeringIndicator.Draw(spriteBatch, displaySubPos, Color.White, origin: steeringIndicator.Origin, rotate: angle,
scale: new Vector2(dir.Length() / steeringIndicator.size.X, 1.0f));
}
else
{
GUI.DrawLine(spriteBatch, displaySubPos, steeringInputPos, Color.LightGray);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringInputPos.X - 5, (int)steeringInputPos.Y - 5, 10, 10), Color.White);
}
if (velRect.Contains(PlayerInput.MousePosition))
{
GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringInputPos.X - 4, (int)steeringInputPos.Y - 4, 8, 8), GUI.Style.Red, thickness: 2);
}
}
else if (posToMaintain.HasValue && !LevelStartSelected && !LevelEndSelected)
{
Sonar sonar = item.GetComponent<Sonar>();
if (sonar != null && controlledSub != null)
{
Vector2 displayPosToMaintain = ((posToMaintain.Value - sonar.DisplayOffset * sonar.Zoom - controlledSub.WorldPosition)) / sonar.Range * sonar.DisplayRadius * sonar.Zoom;
displayPosToMaintain.Y = -displayPosToMaintain.Y;
displayPosToMaintain = displayPosToMaintain.ClampLength(velRect.Width / 2);
displayPosToMaintain = steerArea.Rect.Center.ToVector2() + displayPosToMaintain;
Color crosshairColor = GUI.Style.Orange * (0.5f + ((float)Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) / 4.0f);
if (maintainPosIndicator != null)
{
maintainPosIndicator.Draw(spriteBatch, displayPosToMaintain, crosshairColor, scale: 0.5f * sonar.Zoom);
}
else
{
float crossHairSize = 8.0f;
GUI.DrawLine(spriteBatch, displayPosToMaintain + Vector2.UnitY * crossHairSize, displayPosToMaintain - Vector2.UnitY * crossHairSize, crosshairColor, width: 3);
GUI.DrawLine(spriteBatch, displayPosToMaintain + Vector2.UnitX * crossHairSize, displayPosToMaintain - Vector2.UnitX * crossHairSize, crosshairColor, width: 3);
}
if (maintainPosOriginIndicator != null)
{
maintainPosOriginIndicator.Draw(spriteBatch, displaySubPos, GUI.Style.Orange, scale: 0.5f * sonar.Zoom);
}
else
{
GUI.DrawRectangle(spriteBatch, new Rectangle((int)displaySubPos.X - 5, (int)displaySubPos.Y - 5, 10, 10), GUI.Style.Orange);
}
}
}
//map velocity from rectangle to circle
Vector2 unitTargetVel = targetVelocity / 100.0f;
Vector2 steeringPos = new Vector2(
targetVelocity.X * 0.9f * (float)Math.Sqrt(1.0f - 0.5f * unitTargetVel.Y * unitTargetVel.Y),
-targetVelocity.Y * 0.9f * (float)Math.Sqrt(1.0f - 0.5f * unitTargetVel.X * unitTargetVel.X));
steeringPos += displaySubPos;
if (steeringIndicator != null)
{
Vector2 dir = steeringPos - displaySubPos;
float angle = (float)Math.Atan2(dir.Y, dir.X);
steeringIndicator.Draw(spriteBatch, displaySubPos, Color.Gray, origin: steeringIndicator.Origin, rotate: angle,
scale: new Vector2(dir.Length() / steeringIndicator.size.X, 0.7f));
}
else
{
GUI.DrawLine(spriteBatch,
displaySubPos,
steeringPos,
Color.CadetBlue, 0, 2);
}
}
public void DebugDrawHUD(SpriteBatch spriteBatch, Vector2 transducerCenter, float displayScale, float displayRadius, Vector2 center)
{
if (SteeringPath == null) return;
Vector2 prevPos = Vector2.Zero;
foreach (WayPoint wp in SteeringPath.Nodes)
{
Vector2 pos = (wp.Position - transducerCenter) * displayScale;
if (pos.Length() > displayRadius) continue;
pos.Y = -pos.Y;
pos += center;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X - 3 / 2, (int)pos.Y - 3, 6, 6), (SteeringPath.CurrentNode == wp) ? Color.LightGreen : GUI.Style.Green, false);
if (prevPos != Vector2.Zero)
{
GUI.DrawLine(spriteBatch, pos, prevPos, GUI.Style.Green);
}
prevPos = pos;
}
foreach (ObstacleDebugInfo obstacle in debugDrawObstacles)
{
Vector2 pos1 = (obstacle.Point1 - transducerCenter) * displayScale;
pos1.Y = -pos1.Y;
pos1 += center;
Vector2 pos2 = (obstacle.Point2 - transducerCenter) * displayScale;
pos2.Y = -pos2.Y;
pos2 += center;
GUI.DrawLine(spriteBatch,
pos1,
pos2,
GUI.Style.Red * 0.6f, width: 3);
if (obstacle.Intersection.HasValue)
{
Vector2 intersectionPos = (obstacle.Intersection.Value - transducerCenter) *displayScale;
intersectionPos.Y = -intersectionPos.Y;
intersectionPos += center;
GUI.DrawRectangle(spriteBatch, intersectionPos - Vector2.One * 2, Vector2.One * 4, GUI.Style.Red);
}
Vector2 obstacleCenter = (pos1 + pos2) / 2;
if (obstacle.AvoidStrength.LengthSquared() > 0.01f)
{
GUI.DrawLine(spriteBatch,
obstacleCenter,
obstacleCenter + new Vector2(obstacle.AvoidStrength.X, -obstacle.AvoidStrength.Y) * 100,
Color.Lerp(GUI.Style.Green, GUI.Style.Orange, obstacle.Dot), width: 2);
}
}
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (swapDestinationOrder == null)
{
swapDestinationOrder = item.Submarine != null && item.Submarine.FlippedX;
if (swapDestinationOrder.Value)
{
levelStartTickBox.RectTransform.SetAsLastChild();
}
}
if (steerArea.Rect.Contains(PlayerInput.MousePosition))
{
if (!PlayerInput.KeyDown(InputType.Deselect) && !PlayerInput.KeyHit(InputType.Deselect))
{
Character.DisableControls = true;
}
}
dockingContainer.Visible = DockingModeEnabled;
statusContainer.Visible = !DockingModeEnabled;
if (DockingModeEnabled && ActiveDockingSource != null)
{
if (Math.Abs(ActiveDockingSource.Item.WorldPosition.X - DockingTarget.Item.WorldPosition.X) < ActiveDockingSource.DistanceTolerance.X &&
Math.Abs(ActiveDockingSource.Item.WorldPosition.Y - DockingTarget.Item.WorldPosition.Y) < ActiveDockingSource.DistanceTolerance.Y)
{
dockingButton.Text = dockText;
if (dockingButton.FlashTimer <= 0.0f)
{
dockingButton.Flash(GUI.Style.Blue, 0.5f, useCircularFlash: true);
dockingButton.Pulsate(Vector2.One, Vector2.One * 1.2f, dockingButton.FlashTimer);
}
}
}
else if (DockingSources.Any(d => d.Docked))
{
dockingButton.Text = undockText;
dockingContainer.Visible = true;
statusContainer.Visible = false;
if (dockingButton.FlashTimer <= 0.0f)
{
dockingButton.Flash(GUI.Style.Orange, useCircularFlash: true);
dockingButton.Pulsate(Vector2.One, Vector2.One * 1.2f, dockingButton.FlashTimer);
}
}
else
{
dockingButton.Text = dockText;
}
if (Voltage < MinVoltage)
{
tipContainer.Visible = true;
tipContainer.Text = noPowerTip;
return;
}
tipContainer.Visible = AutoPilot;
if (AutoPilot)
{
if (maintainPos)
{
tipContainer.Text = autoPilotMaintainPosTip;
}
else if (LevelStartSelected)
{
tipContainer.Text = autoPilotLevelStartTip;
}
else if (LevelEndSelected)
{
tipContainer.Text = autoPilotLevelEndTip;
}
if (DockingModeEnabled && DockingTarget != null)
{
posToMaintain += ConvertUnits.ToDisplayUnits(DockingTarget.Item.Submarine.Velocity) * deltaTime;
}
}
pressureWarningText.Visible = item.Submarine != null && item.Submarine.AtDamageDepth && Timing.TotalTime % 1.0f < 0.5f;
if (Vector2.DistanceSquared(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerRadius * steerRadius)
{
if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen && !GameSession.IsInfoFrameOpen)
{
Vector2 displaySubPos = (-sonar.DisplayOffset * sonar.Zoom) / sonar.Range * sonar.DisplayRadius * sonar.Zoom;
displaySubPos.Y = -displaySubPos.Y;
displaySubPos = steerArea.Rect.Center.ToVector2() + displaySubPos;
Vector2 inputPos = PlayerInput.MousePosition - displaySubPos;
inputPos.Y = -inputPos.Y;
if (AutoPilot && !LevelStartSelected && !LevelEndSelected)
{
posToMaintain = controlledSub != null ?
controlledSub.WorldPosition + inputPos / sonar.DisplayRadius * sonar.Range / sonar.Zoom :
item.Submarine == null ? item.WorldPosition : item.Submarine.WorldPosition;
}
else
{
SteeringInput = inputPos;
}
unsentChanges = true;
user = Character.Controlled;
}
}
if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
{
steeringAdjustSpeed = character == null ? 0.2f : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f);
Vector2 input = Vector2.Zero;
if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Up)) { input += Vector2.UnitY; }
if (PlayerInput.KeyDown(InputType.Down)) { input -= Vector2.UnitY; }
if (PlayerInput.KeyDown(InputType.Run))
{
SteeringInput += input * deltaTime * 200;
inputCumulation = 0;
keyboardInput = Vector2.Zero;
unsentChanges = true;
}
else
{
float step = deltaTime * 5;
if (input.Length() > 0)
{
inputCumulation += step;
}
else
{
inputCumulation -= step;
}
float maxCumulation = 1;
inputCumulation = MathHelper.Clamp(inputCumulation, 0, maxCumulation);
float length = MathHelper.Lerp(0, 0.2f, MathUtils.InverseLerp(0, maxCumulation, inputCumulation));
var normalizedInput = Vector2.Normalize(input);
if (MathUtils.IsValid(normalizedInput))
{
keyboardInput += normalizedInput * length;
}
if (keyboardInput.LengthSquared() > 0.01f)
{
SteeringInput += keyboardInput;
unsentChanges = true;
user = Character.Controlled;
keyboardInput *= MathHelper.Clamp(1 - step, 0, 1);
}
}
}
else
{
inputCumulation = 0;
keyboardInput = Vector2.Zero;
}
if (!UseAutoDocking) { return; }
if (checkConnectedPortsTimer <= 0.0f)
{
Connection dockingConnection = item.Connections?.FirstOrDefault(c => c.Name == "toggle_docking");
if (dockingConnection != null)
{
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection);
}
checkConnectedPortsTimer = CheckConnectedPortsInterval;
}
float closestDist = DockingAssistThreshold * DockingAssistThreshold;
DockingModeEnabled = false;
foreach (DockingPort sourcePort in connectedPorts)
{
if (sourcePort.Docked || sourcePort.Item.Submarine == null) { continue; }
if (sourcePort.Item.Submarine != controlledSub) { continue; }
int sourceDir = sourcePort.GetDir();
foreach (DockingPort targetPort in DockingPort.List)
{
if (targetPort.Docked || targetPort.Item.Submarine == null) { continue; }
if (targetPort.Item.Submarine == controlledSub || targetPort.IsHorizontal != sourcePort.IsHorizontal) { continue; }
if (Level.Loaded != null && targetPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
int targetDir = targetPort.GetDir();
if (sourceDir == targetDir) { continue; }
float dist = Vector2.DistanceSquared(sourcePort.Item.WorldPosition, targetPort.Item.WorldPosition);
if (dist < closestDist)
{
DockingModeEnabled = true;
ActiveDockingSource = sourcePort;
DockingTarget = targetPort;
}
}
}
}
private bool NudgeButtonClicked(GUIButton btn, object userdata)
{
if (!MaintainPos || !AutoPilot)
{
AutoPilot = true;
posToMaintain = item.Submarine.WorldPosition;
}
MaintainPos = true;
if (userdata is Vector2)
{
Sonar sonar = item.GetComponent<Sonar>();
Vector2 nudgeAmount = (Vector2)userdata;
if (sonar != null)
{
nudgeAmount *= sonar == null ? 500.0f : 500.0f / sonar.Zoom;
}
PosToMaintain += nudgeAmount;
}
return true;
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
maintainPosIndicator?.Remove();
maintainPosOriginIndicator?.Remove();
steeringIndicator?.Remove();
GameMain.Instance.OnResolutionChanged -= RecreateGUI;
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
msg.Write(AutoPilot);
msg.Write(dockingNetworkMessagePending);
dockingNetworkMessagePending = false;
if (!AutoPilot)
{
//no need to write steering info if autopilot is controlling
msg.Write(steeringInput.X);
msg.Write(steeringInput.Y);
}
else
{
msg.Write(posToMaintain != null);
if (posToMaintain != null)
{
msg.Write(((Vector2)posToMaintain).X);
msg.Write(((Vector2)posToMaintain).Y);
}
else
{
msg.Write(LevelStartSelected);
}
}
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
int msgStartPos = msg.BitPosition;
bool autoPilot = msg.ReadBoolean();
bool dockingButtonClicked = msg.ReadBoolean();
Vector2 newSteeringInput = steeringInput;
Vector2 newTargetVelocity = targetVelocity;
float newSteeringAdjustSpeed = steeringAdjustSpeed;
bool maintainPos = false;
Vector2? newPosToMaintain = null;
bool headingToStart = false;
if (dockingButtonClicked)
{
item.SendSignal(0, "1", "toggle_docking", sender: null);
}
if (autoPilot)
{
maintainPos = msg.ReadBoolean();
if (maintainPos)
{
newPosToMaintain = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
}
else
{
headingToStart = msg.ReadBoolean();
}
}
else
{
newSteeringInput = new Vector2(msg.ReadSingle(), msg.ReadSingle());
newTargetVelocity = new Vector2(msg.ReadSingle(), msg.ReadSingle());
newSteeringAdjustSpeed = msg.ReadSingle();
}
if (correctionTimer > 0.0f)
{
int msgLength = (int)(msg.BitPosition - msgStartPos);
msg.BitPosition = msgStartPos;
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
return;
}
AutoPilot = autoPilot;
if (!AutoPilot)
{
SteeringInput = newSteeringInput;
TargetVelocity = newTargetVelocity;
steeringAdjustSpeed = newSteeringAdjustSpeed;
}
else
{
MaintainPos = newPosToMaintain != null;
posToMaintain = newPosToMaintain;
if (posToMaintain == null)
{
LevelStartSelected = headingToStart;
LevelEndSelected = !headingToStart;
UpdatePath();
}
else
{
LevelStartSelected = false;
LevelEndSelected = false;
}
}
}
private void UpdateGUIElements()
{
steeringModeSwitch.Selected = AutoPilot;
autopilotIndicator.Selected = AutoPilot;
manualPilotIndicator.Selected = !AutoPilot;
maintainPosTickBox.Enabled = AutoPilot;
levelEndTickBox.Enabled = AutoPilot;
levelStartTickBox.Enabled = AutoPilot;
}
}
}