38f1ddb...178a853: v0.8.9.1, removed content folder

This commit is contained in:
Joonas Rikkonen
2019-03-18 19:46:58 +02:00
parent 38f1ddb6fe
commit 6c0679c297
1054 changed files with 151673 additions and 144931 deletions
@@ -1,91 +1,78 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
partial class CharacterInventory : Inventory
{
private static Texture2D icons;
{
public enum Layout
{
Default,
Left,
Right,
Center
}
private enum QuickUseAction
{
None,
Equip,
Unequip,
Drop,
TakeFromContainer,
TakeFromCharacter,
PutToContainer,
PutToCharacter,
UseTreatment
}
private static Dictionary<InvSlotType, Sprite> limbSlotIcons;
private Point screenResolution;
public Vector2[] SlotPositions;
private GUIButton[] useOnSelfButton;
partial void InitProjSpecific()
private Layout layout;
public Layout CurrentLayout
{
useOnSelfButton = new GUIButton[2];
if (icons == null) icons = TextureLoader.FromFile("Content/UI/inventoryIcons.png");
SlotPositions = new Vector2[limbSlots.Length];
int rectWidth = 40, rectHeight = 40;
int spacing = 10;
for (int i = 0; i < SlotPositions.Length; i++)
get { return layout; }
set
{
switch (i)
{
//head, torso, legs
case 0:
case 1:
case 2:
SlotPositions[i] = new Vector2(
spacing,
GameMain.GraphicsHeight - (spacing + rectHeight) * (3 - i));
break;
//lefthand, righthand
case 3:
case 4:
SlotPositions[i] = new Vector2(
spacing * 2 + rectWidth + (spacing + rectWidth) * (i - 1),
GameMain.GraphicsHeight - (spacing + rectHeight) * 3);
useOnSelfButton[i - 3] = new GUIButton(
new Rectangle((int)SlotPositions[i].X, (int)(SlotPositions[i].Y - spacing - rectHeight),
rectWidth, rectHeight), TextManager.Get("UseItemButton"), "")
{
UserData = i,
OnClicked = UseItemOnSelf
};
break;
//face
case 5:
SlotPositions[i] = new Vector2(
spacing * 2 + rectWidth + (spacing + rectWidth) * (i - 5),
GameMain.GraphicsHeight - (spacing + rectHeight) * 3);
break;
//id card
case 6:
SlotPositions[i] = new Vector2(
spacing * 2 + rectWidth + (spacing + rectWidth) * (i - 5),
GameMain.GraphicsHeight - (spacing + rectHeight) * 3);
break;
default:
SlotPositions[i] = new Vector2(
spacing * 2 + rectWidth + (spacing + rectWidth) * ((i - 7) % 5),
GameMain.GraphicsHeight - (spacing + rectHeight) * ((i > 11) ? 2 : 1));
break;
}
if (layout == value) return;
layout = value;
SetSlotPositions(layout);
}
}
public bool Hidden { get; set; }
private bool UseItemOnSelf(GUIButton button, object obj)
partial void InitProjSpecific(XElement element)
{
if (!(obj is int)) return false;
Hidden = true;
int slotIndex = (int)obj;
if (limbSlotIcons == null)
{
limbSlotIcons = new Dictionary<InvSlotType, Sprite>();
return UseItemOnSelf(slotIndex);
int margin = 2;
limbSlotIcons.Add(InvSlotType.Headset, new Sprite("Content/UI/IconAtlas.png", new Rectangle(384 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.InnerClothes, new Sprite("Content/UI/IconAtlas.png", new Rectangle(512 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Card, new Sprite("Content/UI/IconAtlas.png", new Rectangle(640 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Head, new Sprite("Content/UI/IconAtlas.png", new Rectangle(896 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/IconAtlas.png", new Rectangle(640 + margin, 383 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/IconAtlas.png", new Rectangle(768 + margin, 383 + margin, 128 - margin * 2, 128 - margin * 2)));
}
SlotPositions = new Vector2[SlotTypes.Length];
CurrentLayout = Layout.Default;
SetSlotPositions(layout);
}
protected override void PutItem(Item item, int i, Character user, bool removeItem = true, bool createNetworkEvent = true)
{
base.PutItem(item, i, user, removeItem, createNetworkEvent);
@@ -98,127 +85,400 @@ namespace Barotrauma
CreateSlots();
}
protected override void CreateSlots()
public override void CreateSlots()
{
if (slots == null) slots = new InventorySlot[capacity];
for (int i = 0; i < capacity; i++)
{
InventorySlot prevSlot = slots[i];
Sprite slotSprite = slotSpriteSmall;
Rectangle slotRect = new Rectangle(
(int)(SlotPositions[i].X),
(int)(SlotPositions[i].Y),
(int)(slotSprite.size.X * UIScale), (int)(slotSprite.size.Y * UIScale));
int rectWidth = 40, rectHeight = 40;
if (Items[i] != null)
{
ItemContainer itemContainer = Items[i].GetComponent<ItemContainer>();
if (itemContainer != null)
{
if (itemContainer.InventoryTopSprite != null) slotRect.Width = Math.Max(slotRect.Width, (int)(itemContainer.InventoryTopSprite.size.X * UIScale));
if (itemContainer.InventoryBottomSprite != null) slotRect.Width = Math.Max(slotRect.Width, (int)(itemContainer.InventoryBottomSprite.size.X * UIScale));
}
}
slots[i] = new InventorySlot(slotRect)
{
SubInventoryDir = Math.Sign(HUDLayoutSettings.InventoryAreaUpper.Bottom - slotRect.Center.Y),
Disabled = false,
SlotSprite = slotSprite,
Color = SlotTypes[i] == InvSlotType.Any ? Color.White * 0.2f : Color.White * 0.4f
};
if (prevSlot != null)
{
slots[i].DrawOffset = prevSlot.DrawOffset;
slots[i].Color = prevSlot.Color;
}
if (selectedSlot?.ParentInventory == this && selectedSlot.SlotIndex == i)
{
selectedSlot = new SlotReference(this, slots[i], i, selectedSlot.IsSubSlot, selectedSlot.Inventory);
}
}
AssignQuickUseNumKeys();
highlightedSubInventorySlots.Clear();
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
CalculateBackgroundFrame();
}
protected override void CalculateBackgroundFrame()
{
Rectangle frame = Rectangle.Empty;
for (int i = 0; i < capacity; i++)
{
if (HideSlot(i)) continue;
if (frame == Rectangle.Empty)
{
frame = slots[i].Rect;
continue;
}
frame = Rectangle.Union(frame, slots[i].Rect);
}
frame.Inflate(10, 30);
frame.Location -= new Point(0, 25);
BackgroundFrame = frame;
}
protected override bool HideSlot(int i)
{
if (slots[i].Disabled || (hideEmptySlot[i] && Items[i] == null)) return true;
//no need to draw the right hand slot if the item is in both hands
if (Items[i] != null && SlotTypes[i] == InvSlotType.RightHand && IsInLimbSlot(Items[i], InvSlotType.LeftHand))
{
return true;
}
//don't show the equip slot if the item is also in the default inventory
if (SlotTypes[i] != InvSlotType.Any && Items[i] != null)
{
for (int j = 0; j < capacity; j++)
{
if (SlotTypes[j] == InvSlotType.Any && Items[j] == Items[i]) return true;
}
}
return false;
}
private void SetSlotPositions(Layout layout)
{
int spacing = (int)(10 * UIScale);
Point slotSize = (slotSpriteSmall.size * UIScale).ToPoint();
int bottomOffset = slotSize.Y + spacing * 2 + ContainedIndicatorHeight;
if (slots == null) CreateSlots();
var upperSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.Head;
switch (layout)
{
case Layout.Default:
{
int personalSlotCount = SlotTypes.Count(s => upperSlots.HasFlag(s));
int normalSlotCount = SlotTypes.Count(s => !upperSlots.HasFlag(s));
int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (slotSize.X + spacing) / 2;
int upperX = HUDLayoutSettings.PortraitArea.X - slotSize.X;
//make sure the rightmost normal slot doesn't overlap with the personal slots
x -= Math.Max((x + normalSlotCount * (slotSize.X + spacing)) - (upperX - personalSlotCount * (slotSize.X + spacing)), 0);
for (int i = 0; i < SlotPositions.Length; i++)
{
if (upperSlots.HasFlag(SlotTypes[i]))
{
SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset);
upperX -= slotSize.X + spacing;
}
else
{
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += slotSize.X + spacing;
}
}
}
break;
case Layout.Right:
{
int extraOffset = 0;
int x = HUDLayoutSettings.InventoryAreaLower.Right;
int upperX = HUDLayoutSettings.InventoryAreaLower.Right;
for (int i = 0; i < slots.Length; i++)
{
if (HideSlot(i)) continue;
if (upperSlots.HasFlag(SlotTypes[i]))
{
upperX -= slotSize.X + spacing;
}
else
{
x -= slotSize.X + spacing;
}
}
int lowerX = x;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (upperSlots.HasFlag(SlotTypes[i]))
{
SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset * 2 - extraOffset - spacing * 2);
upperX += slots[i].Rect.Width + spacing;
}
else
{
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset - extraOffset);
x += slots[i].Rect.Width + spacing;
}
}
x = lowerX;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (!HideSlot(i)) continue;
x -= slots[i].Rect.Width + spacing;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset - extraOffset);
}
}
break;
case Layout.Left:
{
int x = HUDLayoutSettings.InventoryAreaLower.X;
int upperX = x;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (upperSlots.HasFlag(SlotTypes[i]))
{
SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset * 2 - spacing * 2);
upperX += slots[i].Rect.Width + spacing;
}
else
{
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += slots[i].Rect.Width + spacing;
}
}
for (int i = 0; i < SlotPositions.Length; i++)
{
if (!HideSlot(i)) continue;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += slots[i].Rect.Width + spacing;
}
}
break;
case Layout.Center:
{
int columns = 5;
int startX = (GameMain.GraphicsWidth / 2) - (slotSize.X * columns + spacing * (columns - 1)) / 2;
int startY = GameMain.GraphicsHeight / 2 - (slotSize.Y * 2);
int x = startX, y = startY;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (SlotTypes[i] == InvSlotType.Card || SlotTypes[i] == InvSlotType.Headset || SlotTypes[i] == InvSlotType.InnerClothes)
{
SlotPositions[i] = new Vector2(x, y);
x += slots[i].Rect.Width + spacing;
}
}
y += slots[0].Rect.Height + spacing + ContainedIndicatorHeight + slots[0].EquipButtonRect.Height;
x = startX;
int n = 0;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (SlotTypes[i] != InvSlotType.Card && SlotTypes[i] != InvSlotType.Headset && SlotTypes[i] != InvSlotType.InnerClothes)
{
SlotPositions[i] = new Vector2(x, y);
x += slots[i].Rect.Width + spacing;
n++;
if (n >= columns)
{
x = startX;
y += slots[i].Rect.Height + spacing + ContainedIndicatorHeight + slots[i].EquipButtonRect.Height;
n = 0;
}
}
}
}
break;
}
CreateSlots();
if (layout == Layout.Default)
{
HUDLayoutSettings.InventoryTopY = slots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale);
}
}
protected override void ControlInput(Camera cam)
{
base.ControlInput(cam);
// Ignore the background frame of this object in purpose, because it encompasses half of the screen.
if (highlightedSubInventorySlots.Any(i => i.Inventory != null && i.Inventory.BackgroundFrame.Contains(PlayerInput.MousePosition)))
{
cam.Freeze = true;
}
}
public override void Update(float deltaTime, Camera cam, bool isSubInventory = false)
{
if (!AccessibleWhenAlive && !character.IsDead)
{
syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f);
return;
}
base.Update(deltaTime, cam);
bool hoverOnInventory = GUI.MouseOn == null &&
((selectedSlot != null && selectedSlot.IsSubSlot) || (draggingItem != null && (draggingSlot == null || !draggingSlot.MouseOn())));
if (CharacterHealth.OpenHealthWindow != null) hoverOnInventory = true;
if (hoverOnInventory) HideTimer = 0.5f;
if (HideTimer > 0.0f) HideTimer -= deltaTime;
for (int i = 0; i < capacity; i++)
{
Rectangle slotRect = new Rectangle(
(int)(SlotPositions[i].X + DrawOffset.X),
(int)(SlotPositions[i].Y + DrawOffset.Y),
rectWidth, rectHeight);
slots[i] = new InventorySlot(slotRect);
slots[i].Disabled = false;
slots[i].Color = limbSlots[i] == InvSlotType.Any ? Color.White * 0.2f : Color.White * 0.4f;
}
MergeSlots();
}
public override void Update(float deltaTime, bool isSubInventory = false)
{
base.Update(deltaTime);
if (doubleClickedItem != null)
{
bool wasPut = false;
if (doubleClickedItem.ParentInventory != this)
if (Items[i] != null && Items[i] != draggingItem && Character.Controlled?.Inventory == this &&
GUI.KeyboardDispatcher.Subscriber == null &&
slots[i].QuickUseKey != Keys.None && PlayerInput.KeyHit(slots[i].QuickUseKey))
{
wasPut = TryPutItem(doubleClickedItem, Character.Controlled, doubleClickedItem.AllowedSlots, true);
QuickUseItem(Items[i], true, false, true);
}
}
List<SlotReference> hideSubInventories = new List<SlotReference>();
foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots)
{
if (highlightedSubInventorySlot.ParentInventory == this)
{
UpdateSubInventory(deltaTime, highlightedSubInventorySlot.SlotIndex, cam);
}
Rectangle hoverArea = GetSubInventoryHoverArea(highlightedSubInventorySlot);
if (highlightedSubInventorySlot.Inventory?.slots == null || (!hoverArea.Contains(PlayerInput.MousePosition)))
{
hideSubInventories.Add(highlightedSubInventorySlot);
}
else
{
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
if (selectedContainer != null && selectedContainer.Inventory != null)
highlightedSubInventorySlot.Inventory.HideTimer = 1.0f;
}
}
if (doubleClickedItem != null)
{
QuickUseItem(doubleClickedItem, true, true, true);
}
//activate the subinventory of the currently selected slot
if (selectedSlot?.ParentInventory == this)
{
var subInventory = GetSubInventory(selectedSlot.SlotIndex);
if (subInventory != null)
{
selectedSlot.Inventory = subInventory;
if (!highlightedSubInventorySlots.Any(s => s.Inventory == subInventory))
{
wasPut = selectedContainer.Inventory.TryPutItem(doubleClickedItem, Character.Controlled, doubleClickedItem.AllowedSlots, true);
}
else if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
{
wasPut = character.SelectedCharacter.Inventory.TryPutItem(doubleClickedItem, Character.Controlled, doubleClickedItem.AllowedSlots, true);
}
else if (character.SelectedBy != null && Character.Controlled == character.SelectedBy && character.SelectedBy.Inventory != null)
{
wasPut = character.SelectedBy.Inventory.TryPutItem(doubleClickedItem, Character.Controlled, doubleClickedItem.AllowedSlots, true);
}
else //doubleclicked and no other inventory is selected
{
//not equipped -> attempt to equip
if (IsInLimbSlot(doubleClickedItem, InvSlotType.Any))
var slot = selectedSlot;
highlightedSubInventorySlots.Add(selectedSlot);
UpdateSubInventory(deltaTime, selectedSlot.SlotIndex, cam);
//hide previously opened subinventories if this one overlaps with them
Rectangle hoverArea = GetSubInventoryHoverArea(slot);
foreach (SlotReference highlightedSubInventorySlot in highlightedSubInventorySlots)
{
for (int i = 0; i < capacity; i++)
if (highlightedSubInventorySlot == slot) continue;
if (hoverArea.Intersects(GetSubInventoryHoverArea(highlightedSubInventorySlot)))
{
if (limbSlots[i] == InvSlotType.Any || !doubleClickedItem.AllowedSlots.Any(a => a.HasFlag(limbSlots[i]))) continue;
wasPut = TryPutItem(doubleClickedItem, i, true, false, Character.Controlled, true);
if (wasPut) break;
hideSubInventories.Add(highlightedSubInventorySlot);
highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
}
}
//equipped -> attempt to unequip
else if (doubleClickedItem.AllowedSlots.Contains(InvSlotType.Any))
}
}
}
foreach (var subInventorySlot in hideSubInventories)
{
if (subInventorySlot.Inventory == null) continue;
subInventorySlot.Inventory.HideTimer -= deltaTime;
if (subInventorySlot.Inventory.HideTimer <= 0.0f)
{
highlightedSubInventorySlots.Remove(subInventorySlot);
}
}
for (int i = 0; i < capacity; i++)
{
if (Items[i] != null && Items[i].AllowedSlots.Any(a => a != InvSlotType.Any))
{
slots[i].EquipButtonState = slots[i].EquipButtonRect.Contains(PlayerInput.MousePosition) ?
GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None;
if (PlayerInput.LeftButtonHeld() && PlayerInput.RightButtonHeld())
{
slots[i].EquipButtonState = GUIComponent.ComponentState.None;
}
if (slots[i].EquipButtonState != GUIComponent.ComponentState.Hover)
{
slots[i].QuickUseTimer = Math.Max(0.0f, slots[i].QuickUseTimer - deltaTime * 5.0f);
continue;
}
var quickUseAction = GetQuickUseAction(Items[i], allowEquip: true, allowInventorySwap: false, allowApplyTreatment: false);
slots[i].QuickUseButtonToolTip = quickUseAction == QuickUseAction.None ?
"" : TextManager.Get("QuickUseAction." + quickUseAction.ToString());
//equipped item that can't be put in the inventory, use delayed dropping
if (quickUseAction == QuickUseAction.Drop)
{
slots[i].QuickUseButtonToolTip = "Hold to unequip";
if (PlayerInput.LeftButtonHeld())
{
wasPut = TryPutItem(doubleClickedItem, Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true);
slots[i].QuickUseTimer = Math.Max(0.1f, slots[i].QuickUseTimer + deltaTime);
if (slots[i].QuickUseTimer >= 1.0f)
{
CreateNetworkEvent();
Items[i].Drop(Character.Controlled);
}
}
else
{
slots[i].QuickUseTimer = Math.Max(0.0f, slots[i].QuickUseTimer - deltaTime * 5.0f);
}
}
}
if (wasPut)
{
for (int i = 0; i < capacity; i++)
else
{
if (Items[i] == doubleClickedItem) slots[i].ShowBorderHighlight(Color.Green, 0.1f, 0.9f);
}
}
draggingItem = null;
GUI.PlayUISound(wasPut ? GUISoundType.PickItem : GUISoundType.PickItemFail);
}
if (highlightedSubInventorySlot != null)
{
if (highlightedSubInventorySlot.Inventory == this)
{
UpdateSubInventory(deltaTime, highlightedSubInventorySlot.SlotIndex);
}
if (highlightedSubInventory.slots == null ||
(!highlightedSubInventorySlot.Slot.InteractRect.Contains(PlayerInput.MousePosition) && !highlightedSubInventory.slots.Any(s => s.InteractRect.Contains(PlayerInput.MousePosition))))
{
highlightedSubInventory = null;
highlightedSubInventorySlot = null;
}
}
else
{
if (selectedSlot?.Inventory == this)
{
var subInventory = GetSubInventory(selectedSlot.SlotIndex);
if (subInventory != null)
{
highlightedSubInventory = subInventory;
highlightedSubInventorySlot = selectedSlot;
UpdateSubInventory(deltaTime, highlightedSubInventorySlot.SlotIndex);
}
}
}
if (character == Character.Controlled)
{
for (int i = 0; i < capacity; i++)
{
if ((selectedSlot == null || selectedSlot.SlotIndex != i) &&
Items[i] != null && Items[i].CanUseOnSelf && character.HasSelectedItem(Items[i]))
{
//-3 because selected items are in slots 3 and 4 (hands)
useOnSelfButton[i - 3].Update(deltaTime);
if (PlayerInput.LeftButtonDown()) slots[i].EquipButtonState = GUIComponent.ComponentState.Pressed;
if (PlayerInput.LeftButtonClicked())
{
QuickUseItem(Items[i], allowEquip: true, allowInventorySwap: false, allowApplyTreatment: false);
}
}
}
}
//cancel dragging if too far away from the container of the dragged item
if (draggingItem != null)
@@ -228,8 +488,7 @@ namespace Barotrauma
if (rootContainer != null)
{
rootInventory = rootContainer.ParentInventory != null ?
rootContainer.ParentInventory : rootContainer.GetComponent<ItemContainer>().Inventory;
rootInventory = rootContainer.ParentInventory ?? rootContainer.GetComponent<ItemContainer>().Inventory;
}
if (rootInventory != null &&
@@ -237,98 +496,268 @@ namespace Barotrauma
rootInventory.Owner != Character.Controlled.SelectedConstruction &&
rootInventory.Owner != Character.Controlled.SelectedCharacter)
{
draggingItem = null;
}
}
doubleClickedItem = null;
}
private void MergeSlots()
{
for (int i = 0; i < capacity - 1; i++)
{
slots[i].State = GUIComponent.ComponentState.None;
if (slots[i].Disabled || Items[i] == null) continue;
for (int n = i + 1; n < capacity; n++)
{
if (Items[n] == Items[i])
//allow interacting if the container is linked to the item the character is interacting with
if (!(rootContainer != null &&
rootContainer.DisplaySideBySideWhenLinked &&
Character.Controlled.SelectedConstruction != null &&
rootContainer.linkedTo.Contains(Character.Controlled.SelectedConstruction)))
{
slots[i].Rect = Rectangle.Union(slots[i].Rect, slots[n].Rect);
slots[i].InteractRect = Rectangle.Union(slots[i].InteractRect, slots[n].InteractRect);
slots[n].Disabled = true;
draggingItem = null;
}
}
}
highlightedSubInventory = null;
highlightedSubInventorySlot = null;
selectedSlot = null;
doubleClickedItem = null;
}
private void AssignQuickUseNumKeys()
{
int num = 1;
for (int i = 0; i < slots.Length; i++)
{
if (HideSlot(i))
{
slots[i].QuickUseKey = Keys.None;
continue;
}
if (SlotTypes[i] == InvSlotType.Any)
{
slots[i].QuickUseKey = Keys.D0 + num % 10;
num++;
}
}
}
private QuickUseAction GetQuickUseAction(Item item, bool allowEquip, bool allowInventorySwap, bool allowApplyTreatment)
{
if (allowApplyTreatment && CharacterHealth.OpenHealthWindow != null)
{
return QuickUseAction.UseTreatment;
}
if (item.ParentInventory != this)
{
//in another inventory -> attempt to place in the character's inventory
if (allowInventorySwap)
{
return item.ParentInventory is CharacterInventory ?
QuickUseAction.TakeFromCharacter : QuickUseAction.TakeFromContainer;
}
}
else
{
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
if (selectedContainer != null && selectedContainer.Inventory != null && allowInventorySwap)
{
//player has selected the inventory of another item -> attempt to move the item there
return QuickUseAction.PutToContainer;
}
else if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null && allowInventorySwap)
{
//player has selected the inventory of another character -> attempt to move the item there
return QuickUseAction.PutToCharacter;
}
else if (character.SelectedBy != null && Character.Controlled == character.SelectedBy &&
character.SelectedBy.Inventory != null && allowInventorySwap)
{
return QuickUseAction.TakeFromCharacter;
}
else if (allowEquip) //doubleclicked and no other inventory is selected
{
//not equipped -> attempt to equip
if (!character.HasEquippedItem(item))
{
return QuickUseAction.Equip;
}
//equipped -> attempt to unequip
else if (item.AllowedSlots.Contains(InvSlotType.Any))
{
return QuickUseAction.Unequip;
}
else
{
return QuickUseAction.Drop;
}
}
}
return QuickUseAction.None;
}
private void QuickUseItem(Item item, bool allowEquip, bool allowInventorySwap, bool allowApplyTreatment)
{
var quickUseAction = GetQuickUseAction(item, allowEquip, allowInventorySwap, allowApplyTreatment);
bool success = false;
switch (quickUseAction)
{
case QuickUseAction.Equip:
//attempt to put in a free slot first
for (int i = 0; i < capacity; i++)
{
if (Items[i] != null) continue;
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) continue;
success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) break;
}
if (!success)
{
for (int i = 0; i < capacity; i++)
{
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) continue;
//something else already equipped in the slot, attempt to unequip it
if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any))
{
TryPutItem(Items[i], Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true);
}
success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) break;
}
}
break;
case QuickUseAction.Unequip:
if (item.AllowedSlots.Contains(InvSlotType.Any))
{
success = TryPutItem(item, Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true);
}
break;
case QuickUseAction.UseTreatment:
CharacterHealth.OpenHealthWindow?.OnItemDropped(item, ignoreMousePos: true);
return;
case QuickUseAction.Drop:
//do nothing, the item is dropped after a delay
return;
case QuickUseAction.PutToCharacter:
if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
{
//player has selected the inventory of another character -> attempt to move the item there
success = character.SelectedCharacter.Inventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true);
}
break;
case QuickUseAction.PutToContainer:
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
if (selectedContainer != null && selectedContainer.Inventory != null)
{
//player has selected the inventory of another item -> attempt to move the item there
success = selectedContainer.Inventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true);
}
break;
case QuickUseAction.TakeFromCharacter:
if (character.SelectedBy != null && Character.Controlled == character.SelectedBy &&
character.SelectedBy.Inventory != null)
{
//item is in the inventory of another character -> attempt to get the item from there
success = character.SelectedBy.Inventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true);
}
break;
case QuickUseAction.TakeFromContainer:
success = TryPutItem(item, Character.Controlled, item.AllowedSlots, true);
break;
}
if (success)
{
for (int i = 0; i < capacity; i++)
{
if (Items[i] == item) slots[i].ShowBorderHighlight(Color.Green, 0.1f, 0.4f);
}
}
draggingItem = null;
GUI.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail);
}
public void DrawOwn(SpriteBatch spriteBatch)
{
if (!AccessibleWhenAlive && !character.IsDead) return;
if (slots == null) CreateSlots();
Rectangle slotRect = new Rectangle(0, 0, 40, 40);
for (int i = 0; i < capacity; i++)
if (GameMain.GraphicsWidth != screenResolution.X ||
GameMain.GraphicsHeight != screenResolution.Y ||
prevUIScale != UIScale ||
prevHUDScale != GUI.Scale)
{
slotRect.X = (int)(SlotPositions[i].X + DrawOffset.X);
slotRect.Y = (int)(SlotPositions[i].Y + DrawOffset.Y);
SetSlotPositions(layout);
prevUIScale = UIScale;
prevHUDScale = GUI.Scale;
}
if (i == 1) //head
{
spriteBatch.Draw(icons, new Vector2(slotRect.Center.X, slotRect.Center.Y),
new Rectangle(0, 0, 56, 128), Color.White * 0.7f, 0.0f,
new Vector2(28.0f, 64.0f), Vector2.One,
SpriteEffects.None, 0.1f);
}
else if (i == 3 || i == 4)
{
spriteBatch.Draw(icons, new Vector2(slotRect.Center.X, slotRect.Center.Y),
new Rectangle(92, 41 * (4 - i), 36, 40), Color.White * 0.7f, 0.0f,
new Vector2(18.0f, 20.0f), Vector2.One,
SpriteEffects.None, 0.1f);
}
else if (i == 5)
{
spriteBatch.Draw(icons, new Vector2(slotRect.Center.X, slotRect.Center.Y),
new Rectangle(57, 0, 31, 32), Color.White * 0.7f, 0.0f,
new Vector2(15.0f, 16.0f), Vector2.One,
SpriteEffects.None, 0.1f);
}
else if (i == 6)
{
spriteBatch.Draw(icons, new Vector2(slotRect.Center.X, slotRect.Center.Y),
new Rectangle(62, 36, 22, 18), Color.White * 0.7f, 0.0f,
new Vector2(11.0f, 9.0f), Vector2.One,
SpriteEffects.None, 0.1f);
}
if (layout == Layout.Center)
{
CalculateBackgroundFrame();
GUI.DrawRectangle(spriteBatch, BackgroundFrame, Color.Black * 0.8f, true);
GUI.DrawString(spriteBatch,
new Vector2((int)(BackgroundFrame.Center.X - GUI.Font.MeasureString(character.Name).X / 2), (int)BackgroundFrame.Y + 5),
character.Name, Color.White * 0.9f);
}
base.Draw(spriteBatch);
if (character == Character.Controlled)
InventorySlot highlightedQuickUseSlot = null;
for (int i = 0; i < capacity; i++)
{
for (int i = 0; i < capacity; i++)
if (HideSlot(i)) continue;
if (Items[i] == null ||
(draggingItem == Items[i] && !slots[i].InteractRect.Contains(PlayerInput.MousePosition)) ||
!Items[i].AllowedSlots.Any(a => a != InvSlotType.Any))
{
if ((selectedSlot == null || selectedSlot.SlotIndex != i) &&
Items[i] != null && Items[i].CanUseOnSelf && character.HasSelectedItem(Items[i]))
//draw limb icons on empty slots
if (limbSlotIcons.ContainsKey(SlotTypes[i]))
{
useOnSelfButton[i - 3].Draw(spriteBatch);
var icon = limbSlotIcons[SlotTypes[i]];
icon.Draw(spriteBatch, slots[i].Rect.Center.ToVector2(), Color.White * 0.3f, origin: icon.size / 2, scale: slots[i].Rect.Width / icon.size.X);
}
continue;
}
if (draggingItem == Items[i] && !slots[i].IsHighlighted) continue;
//draw hand icons if the item is equipped in a hand slot
if (IsInLimbSlot(Items[i], InvSlotType.LeftHand))
{
var icon = limbSlotIcons[InvSlotType.LeftHand];
icon.Draw(spriteBatch, new Vector2(slots[i].Rect.X, slots[i].Rect.Bottom), Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.35f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f);
}
if (IsInLimbSlot(Items[i], InvSlotType.RightHand))
{
var icon = limbSlotIcons[InvSlotType.RightHand];
icon.Draw(spriteBatch, new Vector2(slots[i].Rect.Right, slots[i].Rect.Bottom), Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.65f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f);
}
Color color = slots[i].EquipButtonState == GUIComponent.ComponentState.Pressed ? Color.Gray : Color.White * 0.8f;
if (slots[i].EquipButtonState == GUIComponent.ComponentState.Hover)
{
color = Color.White;
highlightedQuickUseSlot = slots[i];
}
var quickUseIndicator = Items[i].AllowedSlots.Any(a => a == InvSlotType.Any) ?
EquipIndicator : DropIndicator;
var quickUseHighlight = Items[i].AllowedSlots.Any(a => a == InvSlotType.Any) ?
EquipIndicatorHighlight : DropIndicatorHighlight;
quickUseIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, quickUseIndicator.Origin, 0, UIScale);
slots[i].QuickUseTimer = Math.Min(slots[i].QuickUseTimer, 1.0f);
if (slots[i].QuickUseTimer > 0.0f)
{
float indicatorFillAmount = character.HasEquippedItem(Items[i]) ? 1.0f - slots[i].QuickUseTimer : slots[i].QuickUseTimer;
quickUseHighlight.DrawTiled(spriteBatch,
slots[i].EquipButtonRect.Center.ToVector2() - quickUseHighlight.Origin * UIScale * 0.85f,
new Vector2(quickUseIndicator.SourceRect.Width * indicatorFillAmount, quickUseIndicator.SourceRect.Height) * UIScale * 0.85f,
null,
color * 0.9f,
null,
Vector2.One * UIScale * 0.85f);
}
else if (character.HasEquippedItem(Items[i]))
{
quickUseHighlight.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color * 0.9f, quickUseHighlight.Origin, 0, UIScale * 0.85f);
}
}
for (int i = 0; i < capacity; i++)
if (highlightedQuickUseSlot != null && !string.IsNullOrEmpty(highlightedQuickUseSlot.QuickUseButtonToolTip))
{
if (slots[i].IsHighlighted)
{
DrawSubInventory(spriteBatch, i);
}
GUIComponent.DrawToolTip(spriteBatch, highlightedQuickUseSlot.QuickUseButtonToolTip, highlightedQuickUseSlot.EquipButtonRect);
}
}
}
@@ -15,10 +15,10 @@ namespace Barotrauma.Items.Components
private Vector2[] GetConvexHullCorners(Rectangle rect)
{
Vector2[] corners = new Vector2[4];
corners[0] = new Vector2(rect.X, rect.Y - rect.Height);
corners[1] = new Vector2(rect.X, rect.Y);
corners[2] = new Vector2(rect.Right, rect.Y);
corners[3] = new Vector2(rect.Right, rect.Y - rect.Height);
corners[0] = new Vector2(rect.X - 1, rect.Y - rect.Height - 1);
corners[1] = new Vector2(rect.X - 1, rect.Y + 1);
corners[2] = new Vector2(rect.Right + 1, rect.Y + 1);
corners[3] = new Vector2(rect.Right + 1, rect.Y - rect.Height - 1);
return corners;
}
@@ -26,10 +26,10 @@ namespace Barotrauma.Items.Components
private void UpdateConvexHulls()
{
doorRect = new Rectangle(
item.Rect.Center.X - (int)(doorSprite.size.X / 2),
item.Rect.Y - item.Rect.Height / 2 + (int)(doorSprite.size.Y / 2.0f),
(int)doorSprite.size.X,
(int)doorSprite.size.Y);
item.Rect.Center.X - (int)(doorSprite.size.X / 2 * item.Scale),
item.Rect.Y - item.Rect.Height / 2 + (int)(doorSprite.size.Y / 2.0f * item.Scale),
(int)(doorSprite.size.X * item.Scale),
(int)(doorSprite.size.Y * item.Scale));
Rectangle rect = doorRect;
if (isHorizontal)
@@ -43,7 +43,7 @@ namespace Barotrauma.Items.Components
if (window.Height > 0 && window.Width > 0)
{
rect.Height = -window.Y;
rect.Height = -(int)(window.Y * item.Scale);
rect.Y += (int)(doorRect.Height * openState);
rect.Height = Math.Max(rect.Height - (rect.Y - doorRect.Y), 0);
@@ -52,12 +52,11 @@ namespace Barotrauma.Items.Components
if (convexHull2 != null)
{
Rectangle rect2 = doorRect;
rect2.Y = rect2.Y + window.Y - window.Height;
rect2.Y = rect2.Y + (int)((window.Y * item.Scale - window.Height * item.Scale));
rect2.Y += (int)(doorRect.Height * openState);
rect2.Y = Math.Min(doorRect.Y, rect2.Y);
rect2.Height = rect2.Y - (doorRect.Y - (int)(doorRect.Height * (1.0f - openState)));
//convexHull2.SetVertices(GetConvexHullCorners(rect2));
if (rect2.Height == 0)
{
@@ -86,7 +85,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing)
{
Color color = (item.IsSelected) ? Color.Green : Color.White;
Color color = Color.White;
if (brokenSprite == null)
{
//broken doors turn black if no broken sprite has been configured
@@ -101,7 +100,7 @@ namespace Barotrauma.Items.Components
weldSpritePos.Y = -weldSpritePos.Y;
weldedSprite.Draw(spriteBatch,
weldSpritePos, Color.White * (stuck / 100.0f), 0.0f, 1.0f);
weldSpritePos, Color.White * (stuck / 100.0f), scale: item.Scale);
}
if (openState == 1.0f)
@@ -122,7 +121,7 @@ namespace Barotrauma.Items.Components
new Rectangle((int) (doorSprite.SourceRect.X + doorSprite.size.X * openState),
(int) doorSprite.SourceRect.Y,
(int) (doorSprite.size.X * (1.0f - openState)), (int) doorSprite.size.Y),
color, 0.0f, doorSprite.Origin, 1.0f, SpriteEffects.None, doorSprite.Depth);
color, 0.0f, doorSprite.Origin, item.Scale, SpriteEffects.None, doorSprite.Depth);
}
if (brokenSprite != null && item.Health < item.Prefab.Health)
@@ -132,7 +131,7 @@ namespace Barotrauma.Items.Components
spriteBatch.Draw(brokenSprite.Texture, pos,
new Rectangle((int)(brokenSprite.SourceRect.X + brokenSprite.size.X * openState), brokenSprite.SourceRect.Y,
(int)(brokenSprite.size.X * (1.0f - openState)), (int)brokenSprite.size.Y),
color * alpha, 0.0f, brokenSprite.Origin, scale, SpriteEffects.None,
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, SpriteEffects.None,
brokenSprite.Depth);
}
}
@@ -148,7 +147,7 @@ namespace Barotrauma.Items.Components
new Rectangle(doorSprite.SourceRect.X,
(int) (doorSprite.SourceRect.Y + doorSprite.size.Y * openState),
(int) doorSprite.size.X, (int) (doorSprite.size.Y * (1.0f - openState))),
color, 0.0f, doorSprite.Origin, 1.0f, SpriteEffects.None, doorSprite.Depth);
color, 0.0f, doorSprite.Origin, item.Scale, SpriteEffects.None, doorSprite.Depth);
}
if (brokenSprite != null && item.Health < item.Prefab.Health)
@@ -158,7 +157,7 @@ namespace Barotrauma.Items.Components
spriteBatch.Draw(brokenSprite.Texture, pos,
new Rectangle(brokenSprite.SourceRect.X, (int)(brokenSprite.SourceRect.Y + brokenSprite.size.Y * openState),
(int)brokenSprite.size.X, (int)(brokenSprite.size.Y * (1.0f - openState))),
color * alpha, 0.0f, brokenSprite.Origin, scale, SpriteEffects.None, brokenSprite.Depth);
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, SpriteEffects.None, brokenSprite.Depth);
}
}
@@ -0,0 +1,56 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma.Items.Components
{
partial class ElectricalDischarger : Powered
{
private static SpriteSheet electricitySprite;
private int frameOffset;
partial void InitProjSpecific()
{
if (electricitySprite == null)
{
electricitySprite = new SpriteSheet("Content/Lights/Electricity.png", 4, 4, new Vector2(0.5f, 0.0f));
}
}
partial void DischargeProjSpecific()
{
PlaySound(ActionType.OnUse, item.WorldPosition);
foreach (Node node in nodes)
{
GameMain.ParticleManager.CreateParticle("swirlysmoke", node.WorldPosition, Vector2.Zero);
}
}
public void DrawElectricity(SpriteBatch spriteBatch)
{
for (int i = 0; i < nodes.Count; i++)
{
if (nodes[i].Length <= 1.0f) continue;
var node = nodes[i];
electricitySprite.Draw(spriteBatch,
(i + frameOffset) % electricitySprite.FrameCount,
new Vector2(node.WorldPosition.X, -node.WorldPosition.Y),
Color.Lerp(Color.LightBlue, Color.White, Rand.Range(0.0f, 1.0f)),
electricitySprite.Origin, -node.Angle - MathHelper.PiOver2,
new Vector2(
Math.Min(node.Length / electricitySprite.FrameSize.X, 1.0f) * Rand.Range(0.5f, 2.0f),
node.Length / electricitySprite.FrameSize.Y) * Rand.Range(1.0f, 1.2f));
}
if (GameMain.DebugDraw)
{
for (int i = 0; i < nodes.Count; i++)
{
if (nodes[i].Length <= 1.0f) continue;
GUI.DrawRectangle(spriteBatch, new Vector2(nodes[i].WorldPosition.X, -nodes[i].WorldPosition.Y), Vector2.One * 5, Color.LightCyan, isFilled: true);
}
}
}
}
}
@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -10,25 +11,38 @@ using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
enum SoundSelectionMode
{
Random,
CharacterSpecific,
ItemSpecific,
All
}
class ItemSound
{
public readonly Sound Sound;
public readonly RoundSound RoundSound;
public readonly ActionType Type;
public string VolumeProperty;
public float VolumeMultiplier
{
get { return RoundSound.Volume; }
}
public float Range
{
get { return RoundSound.Range; }
}
public float VolumeMultiplier;
public readonly float Range;
public readonly bool Loop;
public ItemSound(Sound sound, ActionType type, float range, bool loop = false)
public ItemSound(RoundSound sound, ActionType type, bool loop = false)
{
this.Sound = sound;
this.RoundSound = sound;
this.Type = type;
this.Range = range;
this.Loop = loop;
}
}
@@ -36,71 +50,260 @@ namespace Barotrauma.Items.Components
partial class ItemComponent : ISerializableEntity
{
private Dictionary<ActionType, List<ItemSound>> sounds;
private Dictionary<ActionType, SoundSelectionMode> soundSelectionModes;
private GUIFrame guiFrame;
public GUILayoutSettings DefaultLayout { get; protected set; }
public GUILayoutSettings AlternativeLayout { get; protected set; }
protected GUIFrame GuiFrame
public class GUILayoutSettings
{
get
public Vector2? RelativeSize { get; private set; }
public Point? AbsoluteSize { get; private set; }
public Vector2? RelativeOffset { get; private set; }
public Point? AbsoluteOffset { get; private set; }
public Anchor? Anchor { get; private set; }
public Pivot? Pivot { get; private set; }
public static GUILayoutSettings Load(XElement element)
{
if (guiFrame == null)
var layout = new GUILayoutSettings();
var relativeSize = XMLExtensions.GetAttributeVector2(element, "relativesize", Vector2.Zero);
var absoluteSize = XMLExtensions.GetAttributePoint(element, "absolutesize", new Point(-1000, -1000));
var relativeOffset = XMLExtensions.GetAttributeVector2(element, "relativeoffset", Vector2.Zero);
var absoluteOffset = XMLExtensions.GetAttributePoint(element, "absoluteoffset", new Point(-1000, -1000));
if (relativeSize.Length() > 0)
{
DebugConsole.ThrowError("Error: the component " + name + " in " + item.Name + " doesn't have a GuiFrame component");
guiFrame = new GUIFrame(new Rectangle(0, 0, 100, 100), Color.Black);
layout.RelativeSize = relativeSize;
}
return guiFrame;
if (absoluteSize.X > 0 && absoluteSize.Y > 0)
{
layout.AbsoluteSize = absoluteSize;
}
if (relativeOffset.Length() > 0)
{
layout.RelativeOffset = relativeOffset;
}
if (absoluteOffset.X > -1000 && absoluteOffset.Y > -1000)
{
layout.AbsoluteOffset = absoluteOffset;
}
if (Enum.TryParse(XMLExtensions.GetAttributeString(element, "anchor", ""), out Anchor a))
{
layout.Anchor = a;
}
if (Enum.TryParse(XMLExtensions.GetAttributeString(element, "pivot", ""), out Pivot p))
{
layout.Pivot = p;
}
return layout;
}
public void ApplyTo(RectTransform target)
{
if (RelativeOffset.HasValue)
{
target.RelativeOffset = RelativeOffset.Value;
}
else if (AbsoluteOffset.HasValue)
{
target.AbsoluteOffset = AbsoluteOffset.Value;
}
if (RelativeSize.HasValue)
{
target.RelativeSize = RelativeSize.Value;
}
else if (AbsoluteSize.HasValue)
{
target.NonScaledSize = AbsoluteSize.Value;
}
if (Anchor.HasValue)
{
target.Anchor = Anchor.Value;
}
if (Pivot.HasValue)
{
target.Pivot = Pivot.Value;
}
else
{
target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor);
}
target.RecalculateChildren(true, true);
}
}
public GUIFrame GuiFrame { get; protected set; }
[Serialize(false, false)]
public bool AllowUIOverlap
{
get;
set;
}
private ItemComponent linkToUIComponent;
[Serialize("", false)]
public string LinkUIToComponent
{
get;
set;
}
[Serialize(0, false)]
public int HudPriority
{
get;
private set;
}
private bool useAlternativeLayout;
public bool UseAlternativeLayout
{
get { return useAlternativeLayout; }
set
{
if (AlternativeLayout != null)
{
if (value == useAlternativeLayout) { return; }
useAlternativeLayout = value;
if (useAlternativeLayout)
{
AlternativeLayout?.ApplyTo(GuiFrame.RectTransform);
}
else
{
DefaultLayout?.ApplyTo(GuiFrame.RectTransform);
}
}
}
}
private bool shouldMuffleLooping;
private float lastMuffleCheckTime;
private ItemSound loopingSound;
private int loopingSoundIndex;
public void PlaySound(ActionType type, Vector2 position)
private SoundChannel loopingSoundChannel;
public void PlaySound(ActionType type, Vector2 position, Character user = null)
{
if (loopingSound != null)
{
loopingSoundIndex = loopingSound.Sound.Loop(loopingSoundIndex, GetSoundVolume(loopingSound), position, loopingSound.Range);
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(position.X, position.Y, 0.0f)) > loopingSound.Range * loopingSound.Range)
{
if (loopingSoundChannel != null)
{
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
}
return;
}
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
{
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
}
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
new Vector3(position.X, position.Y, 0.0f),
GetSoundVolume(loopingSound),
SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
loopingSoundChannel.Looping = true;
//TODO: tweak
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
loopingSoundChannel.Far = loopingSound.Range;
}
if (loopingSoundChannel != null)
{
if (Timing.TotalTime > lastMuffleCheckTime + 0.2f)
{
shouldMuffleLooping = SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull);
lastMuffleCheckTime = (float)Timing.TotalTime;
}
loopingSoundChannel.Muffled = shouldMuffleLooping;
loopingSoundChannel.Gain = GetSoundVolume(loopingSound);
loopingSoundChannel.Position = new Vector3(position.X, position.Y, 0.0f);
}
return;
}
List<ItemSound> matchingSounds;
if (!sounds.TryGetValue(type, out matchingSounds)) return;
if (!sounds.TryGetValue(type, out List<ItemSound> matchingSounds)) return;
ItemSound itemSound = null;
if (!Sounds.SoundManager.IsPlaying(loopingSoundIndex))
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{
int index = Rand.Int(matchingSounds.Count);
SoundSelectionMode soundSelectionMode = soundSelectionModes[type];
int index;
if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && user != null)
{
index = user.ID % matchingSounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.ItemSpecific)
{
index = item.ID % matchingSounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.All)
{
foreach (ItemSound sound in matchingSounds)
{
PlaySound(sound, position, user);
}
return;
}
else
{
index = Rand.Int(matchingSounds.Count);
}
itemSound = matchingSounds[index];
PlaySound(matchingSounds[index], position, user);
}
if (itemSound == null) return;
}
private void PlaySound(ItemSound itemSound, Vector2 position, Character user = null)
{
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(position.X, position.Y, 0.0f)) > itemSound.Range * itemSound.Range)
{
return;
}
if (itemSound.Loop)
{
loopingSound = itemSound;
loopingSoundIndex = loopingSound.Sound.Loop(loopingSoundIndex, GetSoundVolume(loopingSound), position, loopingSound.Range);
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
{
loopingSoundChannel.Dispose(); loopingSoundChannel = null;
}
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
new Vector3(position.X, position.Y, 0.0f),
GetSoundVolume(loopingSound),
muffle: SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
loopingSoundChannel.Looping = true;
//TODO: tweak
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
loopingSoundChannel.Far = loopingSound.Range;
}
}
else
{
float volume = GetSoundVolume(itemSound);
if (volume == 0.0f) return;
itemSound.Sound.Play(volume, itemSound.Range, position);
if (volume <= 0.0f) return;
SoundPlayer.PlaySound(itemSound.RoundSound.Sound, volume, itemSound.Range, position, item.CurrentHull);
}
}
public void StopSounds(ActionType type)
{
if (loopingSoundIndex <= 0) return;
if (loopingSound == null) return;
if (loopingSound.Type != type) return;
if (Sounds.SoundManager.IsPlaying(loopingSoundIndex))
if (loopingSoundChannel != null)
{
Sounds.SoundManager.Stop(loopingSoundIndex);
loopingSoundChannel.Dispose();
loopingSoundChannel = null;
loopingSound = null;
loopingSoundIndex = -1;
}
}
@@ -109,8 +312,7 @@ namespace Barotrauma.Items.Components
if (sound == null) return 0.0f;
if (sound.VolumeProperty == "") return 1.0f;
SerializableProperty property = null;
if (properties.TryGetValue(sound.VolumeProperty.ToLowerInvariant(), out property))
if (properties.TryGetValue(sound.VolumeProperty.ToLowerInvariant(), out SerializableProperty property))
{
float newVolume = 0.0f;
try
@@ -123,55 +325,80 @@ namespace Barotrauma.Items.Components
}
newVolume *= sound.VolumeMultiplier;
if (!MathUtils.IsValid(newVolume))
{
DebugConsole.Log("Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
GameAnalyticsManager.AddErrorEventOnce(
"ItemComponent.PlaySound:" + item.Name + GetType().ToString(),
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
return 0.0f;
}
return MathHelper.Clamp(newVolume, 0.0f, 1.0f);
}
return 0.0f;
}
public virtual bool ShouldDrawHUD(Character character)
{
return true;
}
public ItemComponent GetLinkUIToComponent()
{
if (string.IsNullOrEmpty(LinkUIToComponent))
{
return null;
}
foreach (ItemComponent component in item.components)
{
if (component.name.ToLower() == LinkUIToComponent.ToLower())
{
linkToUIComponent = component;
}
}
if (linkToUIComponent == null)
{
DebugConsole.ThrowError("Failed to link the component \"" + Name + "\" to \"" + LinkUIToComponent + "\" in the item \"" + item.Name + "\" - component with a matching name not found.");
}
return linkToUIComponent;
}
public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { }
public virtual void AddToGUIUpdateList() { }
public virtual void AddToGUIUpdateList()
{
GuiFrame?.AddToGUIUpdateList();
}
public virtual void UpdateHUD(Character character) { }
public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { }
private bool LoadElemProjSpecific(XElement subElement)
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "guiframe":
string rectStr = subElement.GetAttributeString("rect", "0.0,0.0,0.5,0.5");
string[] components = rectStr.Split(',');
if (components.Length < 4) break;
Vector4 rect = subElement.GetAttributeVector4("rect", Vector4.One);
if (components[0].Contains(".")) rect.X *= GameMain.GraphicsWidth;
if (components[1].Contains(".")) rect.Y *= GameMain.GraphicsHeight;
if (components[2].Contains(".")) rect.Z *= GameMain.GraphicsWidth;
if (components[3].Contains(".")) rect.W *= GameMain.GraphicsHeight;
string style = subElement.GetAttributeString("style", "");
Vector4 color = subElement.GetAttributeVector4("color", Vector4.One);
Alignment alignment = Alignment.Center;
try
if (subElement.Attribute("rect") != null)
{
alignment = (Alignment)Enum.Parse(typeof(Alignment),
subElement.GetAttributeString("alignment", "Center"), true);
}
catch
{
DebugConsole.ThrowError("Error in " + subElement.Parent + "! \"" + subElement.Parent.Attribute("type").Value + "\" is not a valid alignment");
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - GUIFrame defined as rect, use RectTransform instead.");
break;
}
guiFrame = new GUIFrame(
new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Z, (int)rect.W),
new Color(color.X, color.Y, color.Z) * color.W,
alignment, style);
Color? color = null;
if (subElement.Attribute("color") != null) color = subElement.GetAttributeColor("color", Color.White);
string style = subElement.Attribute("style") == null ?
null : subElement.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(subElement, GUI.Canvas), style, color);
DefaultLayout = GUILayoutSettings.Load(subElement);
break;
case "alternativelayout":
AlternativeLayout = GUILayoutSettings.Load(subElement);
break;
case "itemsound":
case "sound":
string filePath = subElement.GetAttributeString("file", "");
@@ -189,7 +416,6 @@ namespace Barotrauma.Items.Components
}
ActionType type;
try
{
type = (ActionType)Enum.Parse(typeof(ActionType), subElement.GetAttributeString("type", ""), true);
@@ -199,14 +425,20 @@ namespace Barotrauma.Items.Components
DebugConsole.ThrowError("Invalid sound type in " + subElement + "!", e);
break;
}
RoundSound sound = Submarine.LoadRoundSound(subElement);
ItemSound itemSound = new ItemSound(sound, type, subElement.GetAttributeBool("loop", false))
{
VolumeProperty = subElement.GetAttributeString("volumeproperty", "")
};
Sound sound = Sound.Load(filePath);
float range = subElement.GetAttributeFloat("range", 800.0f);
bool loop = subElement.GetAttributeBool("loop", false);
ItemSound itemSound = new ItemSound(sound, type, range, loop);
itemSound.VolumeProperty = subElement.GetAttributeString("volume", "");
itemSound.VolumeMultiplier = subElement.GetAttributeFloat("volumemultiplier", 1.0f);
if (soundSelectionModes == null) soundSelectionModes = new Dictionary<ActionType, SoundSelectionMode>();
if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random)
{
SoundSelectionMode selectionMode = SoundSelectionMode.Random;
Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out selectionMode);
soundSelectionModes[type] = selectionMode;
}
List<ItemSound> soundList = null;
if (!sounds.TryGetValue(itemSound.Type, out soundList))
@@ -1,29 +1,146 @@
using Microsoft.Xna.Framework;
using System;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
partial class ItemContainer : ItemComponent, IDrawableComponent
{
//TODO: shouldn't this be overriding the base method?
private Sprite inventoryTopSprite;
private Sprite inventoryBackSprite;
private Sprite inventoryBottomSprite;
private GUICustomComponent guiCustomComponent;
public Sprite InventoryTopSprite
{
get { return inventoryTopSprite; }
}
public Sprite InventoryBackSprite
{
get { return inventoryBackSprite; }
}
public Sprite InventoryBottomSprite
{
get { return inventoryBottomSprite; }
}
public Sprite ContainedStateIndicator
{
get;
private set;
}
#if DEBUG
[Serialize("0.0,0.0", false), Editable]
#else
[Serialize("0.0,0.0", false)]
#endif
public Vector2 ItemPos { get; set; }
#if DEBUG
[Serialize("0.0,0.0", false), Editable]
#else
[Serialize("0.0,0.0", false)]
#endif
public Vector2 ItemInterval { get; set; }
[Serialize(100, false)]
public int ItemsPerRow { get; set; }
/// <summary>
/// Depth at which the contained sprites are drawn. If not set, the original depth of the item sprites is used.
/// </summary>
[Serialize(-1.0f, false)]
public float ContainedSpriteDepth { get; set; }
private float itemRotation;
[Serialize(0.0f, false)]
public float ItemRotation
{
get { return MathHelper.ToDegrees(itemRotation); }
set { itemRotation = MathHelper.ToRadians(value); }
}
[Serialize(null, false)]
public string UILabel { get; set; }
[Serialize(false, false)]
public bool ShowConditionInContainedStateIndicator
{
get;
set;
}
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "topsprite":
inventoryTopSprite = new Sprite(subElement);
break;
case "backsprite":
inventoryBackSprite = new Sprite(subElement);
break;
case "bottomsprite":
inventoryBottomSprite = new Sprite(subElement);
break;
case "containedstateindicator":
ContainedStateIndicator = new Sprite(subElement);
break;
}
}
if (GuiFrame == null)
{
//if a GUIFrame is not defined in the xml,
//we create a full-screen frame and let the inventory position itself on it
GuiFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null)
{
CanBeFocused = false
};
guiCustomComponent = new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform),
onDraw: (SpriteBatch spriteBatch, GUICustomComponent component) => { Inventory.Draw(spriteBatch); },
onUpdate: null)
{
CanBeFocused = false
};
}
else
{
//if a GUIFrame has been defined, draw the inventory inside it
guiCustomComponent = new GUICustomComponent(new RectTransform(new Vector2(0.9f), GuiFrame.RectTransform, Anchor.Center),
onDraw: (SpriteBatch spriteBatch, GUICustomComponent component) => { Inventory.Draw(spriteBatch); },
onUpdate: null)
{
CanBeFocused = false
};
Inventory.RectTransform = guiCustomComponent.RectTransform;
}
}
public void Draw(SpriteBatch spriteBatch, bool editing = false)
{
if (hideItems || (item.body != null && !item.body.Enabled)) return;
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }
DrawContainedItems(spriteBatch);
}
Vector2 transformedItemPos = itemPos;
Vector2 transformedItemInterval = itemInterval;
public void DrawContainedItems(SpriteBatch spriteBatch)
{
Vector2 transformedItemPos = ItemPos * item.Scale;
Vector2 transformedItemInterval = ItemInterval * item.Scale;
float currentRotation = itemRotation;
if (item.body == null)
{
transformedItemPos = new Vector2(item.Rect.X, item.Rect.Y);
if (item.Submarine != null) transformedItemPos += item.Submarine.DrawPosition;
transformedItemPos = transformedItemPos + itemPos;
transformedItemPos = transformedItemPos + ItemPos * item.Scale;
}
else
{
//item.body.Enabled = true;
Matrix transform = Matrix.CreateRotationZ(item.body.Rotation);
if (item.body.Dir == -1.0f)
@@ -39,30 +156,80 @@ namespace Barotrauma.Items.Components
currentRotation += item.body.Rotation;
}
Vector2 currentItemPos = transformedItemPos;
int i = 0;
foreach (Item containedItem in Inventory.Items)
{
if (containedItem == null) continue;
if (AutoInteractWithContained)
{
containedItem.IsHighlighted = item.IsHighlighted;
item.IsHighlighted = false;
}
if (containedItem.body != null)
{
containedItem.body.FarseerBody.Rotation = currentRotation;
}
containedItem.Sprite.Draw(
spriteBatch,
new Vector2(transformedItemPos.X, -transformedItemPos.Y),
new Vector2(currentItemPos.X, -currentItemPos.Y),
containedItem.GetSpriteColor(),
-currentRotation,
1.0f,
(item.body != null && item.body.Dir == -1) ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
containedItem.Scale,
(item.body != null && item.body.Dir == -1) ? SpriteEffects.FlipHorizontally : SpriteEffects.None,
depth: ContainedSpriteDepth < 0.0f ? containedItem.Sprite.Depth : ContainedSpriteDepth);
transformedItemPos += transformedItemInterval;
foreach (ItemContainer ic in containedItem.GetComponents<ItemContainer>())
{
if (ic.hideItems) continue;
ic.DrawContainedItems(spriteBatch);
}
i++;
if (Math.Abs(transformedItemInterval.X) > 0.001f && Math.Abs(transformedItemInterval.Y) > 0.001f)
{
//interval set on both axes -> use a grid layout
currentItemPos.X += transformedItemInterval.X;
if (i % ItemsPerRow == 0)
{
currentItemPos.X = transformedItemPos.X;
currentItemPos.Y += transformedItemInterval.Y;
}
}
else
{
currentItemPos += transformedItemInterval;
}
}
}
public override void UpdateHUD(Character character)
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
Inventory.Update((float)Timing.Step);
if (Inventory.RectTransform != null)
{
guiCustomComponent.RectTransform.Parent = Inventory.RectTransform;
}
//if the item is in the character's inventory, no need to update the item's inventory
//because the player can see it by hovering the cursor over the item
guiCustomComponent.Visible = item.ParentInventory?.Owner != character && DrawInventory;
if (!guiCustomComponent.Visible) return;
Inventory.Update(deltaTime, cam);
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
/*public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
Inventory.Draw(spriteBatch);
}
//if the item is in the character's inventory, no need to draw the item's inventory
//because the player can see it by hovering the cursor over the item
if (item.ParentInventory?.Owner == character || !DrawInventory) return;
Inventory.Draw(spriteBatch);
}*/
}
}
@@ -1,5 +1,7 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
@@ -10,20 +12,38 @@ namespace Barotrauma.Items.Components
private Color textColor;
private float scrollAmount;
private string scrollingText;
private float scrollPadding;
private int scrollIndex;
private bool needsScrolling;
private float[] charWidths;
[Serialize("0,0,0,0", true)]
public Vector4 Padding
{
get { return TextBlock.Padding; }
set { TextBlock.Padding = value; }
}
private string text;
[Serialize("", true), Editable(100)]
public string Text
{
get { return textBlock.Text.Replace("\n", ""); }
get { return text; }
set
{
if (value == TextBlock.Text || item.Rect.Width < 5) return;
if (value == text || item.Rect.Width < 5) return;
if (textBlock.Rect.Width != item.Rect.Width || textBlock.Rect.Height != item.Rect.Height)
if (TextBlock.Rect.Width != item.Rect.Width || textBlock.Rect.Height != item.Rect.Height)
{
textBlock = null;
}
text = value;
TextBlock.Text = value;
SetScrollingText();
}
}
@@ -48,42 +68,149 @@ namespace Barotrauma.Items.Components
}
}
private bool scrollable;
[Serialize(false, true)]
public bool Scrollable
{
get { return scrollable; }
set
{
scrollable = value;
IsActive = value;
TextBlock.Wrap = !scrollable;
TextBlock.TextAlignment = scrollable ? Alignment.CenterLeft : Alignment.Center;
}
}
[Serialize(20.0f, true)]
public float ScrollSpeed
{
get;
set;
}
private GUITextBlock TextBlock
{
get
{
if (textBlock == null)
{
textBlock = new GUITextBlock(new Rectangle(item.Rect.X, -item.Rect.Y, item.Rect.Width, item.Rect.Height), "",
Color.Transparent, textColor,
Alignment.TopLeft, Alignment.Center,
null, null, true);
textBlock.Font = GUI.SmallFont;
textBlock.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
textBlock.TextDepth = item.Sprite.Depth - 0.0001f;
textBlock.TextScale = TextScale;
textBlock = new GUITextBlock(new RectTransform(item.Rect.Size), "",
textColor: textColor, font: GUI.SmallFont, textAlignment: Alignment.Center, wrap: true, style: null)
{
TextDepth = item.SpriteDepth - 0.0001f,
TextScale = TextScale
};
}
return textBlock;
}
}
public override void Move(Vector2 amount)
{
textBlock.Rect = new Rectangle(item.Rect.X, -item.Rect.Y, item.Rect.Width, item.Rect.Height);
}
public ItemLabel(Item item, XElement element)
: base(item, element)
{
{
}
private void SetScrollingText()
{
if (!scrollable) return;
float totalWidth = textBlock.Font.MeasureString(text).X;
float textAreaWidth = Math.Max(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z, 0);
if (totalWidth >= textAreaWidth)
{
//add enough spaces to fill the rect
//(so the text can scroll entirely out of view before we reset it back to start)
needsScrolling = true;
float spaceWidth = textBlock.Font.MeasureChar(' ').X;
scrollingText = new string(' ', (int)Math.Ceiling(textAreaWidth / spaceWidth)) + text;
}
else
{
//whole text can fit in the textblock, no need to scroll
needsScrolling = false;
scrollingText = text;
scrollAmount = 0.0f;
scrollIndex = 0;
return;
}
//calculate character widths
scrollPadding = 0;
charWidths = new float[scrollingText.Length];
for (int i = 0; i < scrollingText.Length; i++)
{
float charWidth = TextBlock.Font.MeasureChar(scrollingText[i]).X;
scrollPadding = Math.Max(charWidth, scrollPadding);
charWidths[i] = charWidth;
}
scrollIndex = MathHelper.Clamp(scrollIndex, 0, text.Length);
}
public override void Update(float deltaTime, Camera cam)
{
if (!scrollable) return;
if (scrollingText == null)
{
SetScrollingText();
}
if (!needsScrolling) return;
scrollAmount -= deltaTime * ScrollSpeed;
float currLength = 0;
StringBuilder sb = new StringBuilder();
float textAreaWidth = textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z;
for (int i = scrollIndex; i < scrollingText.Length; i++)
{
//first character is out of view -> skip to next character
if (i == scrollIndex && scrollAmount < -charWidths[i])
{
scrollIndex++;
scrollAmount = 0;
if (scrollIndex >= scrollingText.Length) //reached the last character, reset
{
scrollIndex = 0;
break;
}
continue;
}
//reached the right edge, stop adding more character
if (scrollAmount + (currLength + charWidths[i] + scrollPadding) >= textAreaWidth)
{
break;
}
else
{
currLength += charWidths[i];
sb.Append(scrollingText[i]);
}
}
TextBlock.Text = sb.ToString();
}
public void Draw(SpriteBatch spriteBatch, bool editing = false)
{
var drawPos = new Vector2(
item.DrawPosition.X - item.Rect.Width / 2.0f,
-(item.DrawPosition.Y + item.Rect.Height / 2.0f));
Rectangle worldRect = item.WorldRect;
if (worldRect.X > Screen.Selected.Cam.WorldView.Right ||
worldRect.Right < Screen.Selected.Cam.WorldView.X ||
worldRect.Y < Screen.Selected.Cam.WorldView.Y - Screen.Selected.Cam.WorldView.Height ||
worldRect.Y - worldRect.Height > Screen.Selected.Cam.WorldView.Y)
{
return;
}
textBlock.Draw(spriteBatch, drawPos - textBlock.Rect.Location.ToVector2());
textBlock.TextOffset = drawPos - textBlock.Rect.Location.ToVector2() + new Vector2(scrollAmount + scrollPadding, 0.0f);
textBlock.DrawManually(spriteBatch);
}
}
}
@@ -0,0 +1,17 @@
using Barotrauma.Networking;
using Lidgren.Network;
namespace Barotrauma.Items.Components
{
partial class LevelResource : ItemComponent, IServerSerializable
{
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
DeattachTimer = msg.ReadSingle();
if (deattachTimer >= DeattachDuration)
{
holdable.DeattachFromWall();
}
}
}
}
@@ -9,16 +9,20 @@ namespace Barotrauma.Items.Components
partial class LightComponent : Powered, IServerSerializable, IDrawableComponent
{
private LightSource light;
public LightSource Light
{
get { return light; }
}
public void Draw(SpriteBatch spriteBatch, bool editing = false)
{
if (light.LightSprite != null && (item.body == null || item.body.Enabled))
if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f)
{
light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, 0.0f, 1.0f, Microsoft.Xna.Framework.Graphics.SpriteEffects.None, item.Sprite.Depth - 0.0001f);
light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, 0.0f, 1.0f, Microsoft.Xna.Framework.Graphics.SpriteEffects.None, item.SpriteDepth - 0.0001f);
}
}
public override void FlipX()
public override void FlipX(bool relativeToSub)
{
if (light?.LightSprite != null)
{
@@ -1,27 +1,77 @@
using Barotrauma.Networking;
using Lidgren.Network;
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
{
GUIProgressBar progressBar;
GUIButton activateButton;
private GUIButton activateButton;
private GUIComponent inputInventoryHolder, outputInventoryHolder;
private GUICustomComponent inputInventoryOverlay;
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
private GUIComponent inSufficientPowerWarning;
partial void InitProjSpecific(XElement element)
{
GuiFrame.Draw(spriteBatch);
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
Stretch = true,
RelativeSpacing = 0.02f
};
inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.2f, 0.7f), paddedFrame.RectTransform), style: null);
inputInventoryOverlay = new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawOverLay, null)
{
CanBeFocused = false
};
activateButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.1f), paddedFrame.RectTransform),
TextManager.Get("DeconstructorDeconstruct"))
{
OnClicked = ToggleActive
};
inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform), TextManager.Get("DeconstructorNoPower"),
textColor: Color.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow")
{
HoverColor = Color.Black,
IgnoreLayoutGroups = true,
Visible = false,
CanBeFocused = false
};
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.3f), paddedFrame.RectTransform), style: null);
}
public override void AddToGUIUpdateList()
partial void OnItemLoadedProjSpecific()
{
GuiFrame.AddToGUIUpdateList();
inputContainer.AllowUIOverlap = true;
inputContainer.Inventory.RectTransform = inputInventoryHolder.RectTransform;
outputContainer.AllowUIOverlap = true;
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
}
public override void UpdateHUD(Character character)
private void DrawOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{
GuiFrame.Update((float)Timing.Step);
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)),
Color.Green * 0.5f, isFilled: true);
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
inSufficientPowerWarning.Visible = powerConsumption > 0 && voltage < minVoltage;
activateButton.Enabled = !inSufficientPowerWarning.Visible;
}
private bool ToggleActive(GUIButton button, object obj)
@@ -1,25 +1,142 @@
using Microsoft.Xna.Framework;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Engine : Powered
partial class Engine : Powered, IDrawableComponent
{
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
GuiFrame.Draw(spriteBatch);
private float spriteIndex;
GUI.Font.DrawString(spriteBatch, TextManager.Get("Force") + ": " + (int)(targetForce) + " %", new Vector2(GuiFrame.Rect.X + 30, GuiFrame.Rect.Y + 30), Color.White);
private SpriteSheet propellerSprite;
private GUITickBox powerIndicator;
private GUIScrollBar forceSlider;
public float AnimSpeed
{
get;
private set;
}
public override void AddToGUIUpdateList()
partial void InitProjSpecific(XElement element)
{
GuiFrame.AddToGUIUpdateList();
powerIndicator = new GUITickBox(new RectTransform(new Point(30, 30), GuiFrame.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.15f) },
TextManager.Get("EnginePowered"), style: "IndicatorLightGreen")
{
CanBeFocused = false
};
string powerLabel = TextManager.Get("EngineForce");
new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.3f), GuiFrame.RectTransform, Anchor.BottomCenter)
{ RelativeOffset = new Vector2(0.0f, 0.4f) }, "", textAlignment: Alignment.Center)
{
TextGetter = () => { return powerLabel + ": " + (int)(targetForce) + " %"; }
};
var sliderArea = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.25f), GuiFrame.RectTransform, Anchor.BottomCenter)
{ RelativeOffset = new Vector2(0.0f, 0.2f) }, isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1.0f), sliderArea.RectTransform), TextManager.Get("EngineBackwards"),
font: GUI.SmallFont, textAlignment: Alignment.Center);
forceSlider = new GUIScrollBar(new RectTransform(new Vector2(0.6f, 1.0f), sliderArea.RectTransform), barSize: 0.25f, style: "GUISlider")
{
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.Server != null)
{
item.CreateServerEvent(this);
GameServer.Log(Character.Controlled.LogName + " set the force speed of " + item.Name + " to " + (int)(targetForce) + " %", ServerLog.MessageType.ItemInteraction);
}
else if (GameMain.Client != null)
{
correctionTimer = CorrectionDelay;
item.CreateClientEvent(this);
}
return true;
}
};
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1.0f), sliderArea.RectTransform), TextManager.Get("EngineForwards"),
font: GUI.SmallFont, textAlignment: Alignment.Center);
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)
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
GuiFrame.Update(1.0f / 60.0f);
powerIndicator.Selected = hasPower && IsActive;
if (!PlayerInput.LeftButtonHeld())
{
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)
{
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, Color.Red);
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//targetForce can only be adjusted at 10% intervals -> no need for more accuracy than this
msg.WriteRangedInteger(-10, 10, (int)(targetForce / 10.0f));
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{
StartDelayedCorrection(type, msg.ExtractBits(5), sendingTime);
return;
}
targetForce = msg.ReadRangedInteger(-10, 10) * 10.0f;
}
}
}
@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -13,148 +14,309 @@ namespace Barotrauma.Items.Components
private GUIListBox itemList;
private GUIFrame selectedItemFrame;
private GUIProgressBar progressBar;
private GUIButton activateButton;
private GUIComponent inputInventoryHolder, outputInventoryHolder;
private GUICustomComponent inputInventoryOverlay, outputInventoryOverlay;
private FabricableItem selectedItem;
private GUIComponent inSufficientPowerWarning;
private Pair<Rectangle, string> tooltip;
partial void InitProjSpecific()
{
GuiFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
Stretch = true,
RelativeSpacing = 0.02f
};
itemList = new GUIListBox(new Rectangle(0, 0, GuiFrame.Rect.Width / 2 - 20, 0), "", GuiFrame);
itemList.OnSelected = SelectItem;
itemList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), paddedFrame.RectTransform))
{
OnSelected = SelectItem
};
inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.15f), paddedFrame.RectTransform), style: null);
inputInventoryOverlay = new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawInputOverLay, null)
{
CanBeFocused = false
};
var outputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.3f), paddedFrame.RectTransform), isHorizontal: true);
selectedItemFrame = new GUIFrame(new RectTransform(new Vector2(0.75f, 1.0f), outputArea.RectTransform), style: "InnerFrame");
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), outputArea.RectTransform), style: null);
outputInventoryOverlay = new GUICustomComponent(new RectTransform(Vector2.One, outputArea.RectTransform), DrawOutputOverLay, null)
{
CanBeFocused = false
};
foreach (FabricableItem fi in fabricableItems)
{
GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), Color.Transparent, null, itemList)
GUIFrame frame = new GUIFrame(new RectTransform(new Point(itemList.Rect.Width, 50), itemList.Content.RectTransform), style: null)
{
UserData = fi,
Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f),
HoverColor = Color.Gold * 0.2f,
SelectedColor = Color.Gold * 0.5f,
ToolTip = fi.TargetItem.Description
};
GUITextBlock textBlock = new GUITextBlock(
new Rectangle(40, 0, 0, 25),
fi.TargetItem.Name,
Color.Transparent, Color.White,
Alignment.Left, Alignment.Left,
null, frame);
textBlock.ToolTip = fi.TargetItem.Description;
textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f);
if (fi.TargetItem.sprite != null)
GUITextBlock textBlock = new GUITextBlock(new RectTransform(Vector2.Zero, frame.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(50, 0) },
fi.DisplayName)
{
GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), fi.TargetItem.sprite, Alignment.Left, frame);
img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
img.Color = fi.TargetItem.SpriteColor;
img.ToolTip = fi.TargetItem.Description;
ToolTip = fi.TargetItem.Description
};
var itemIcon = fi.TargetItem.InventoryIcon ?? fi.TargetItem.sprite;
if (itemIcon != null)
{
GUIImage img = new GUIImage(new RectTransform(new Point(40, 40), frame.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(3, 0) },
itemIcon, scaleToFit: true)
{
Color = fi.TargetItem.InventoryIconColor,
ToolTip = fi.TargetItem.Description
};
}
}
activateButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.07f), paddedFrame.RectTransform),
TextManager.Get("FabricatorCreate"))
{
OnClicked = StartButtonClicked,
UserData = selectedItem,
Enabled = false
};
inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform), TextManager.Get("FabricatorNoPower"),
textColor: Color.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow")
{
HoverColor = Color.Black,
IgnoreLayoutGroups = true,
Visible = false,
CanBeFocused = false
};
}
partial void OnItemLoadedProjSpecific()
{
inputContainer.AllowUIOverlap = true;
inputContainer.Inventory.RectTransform = inputInventoryHolder.RectTransform;
outputContainer.AllowUIOverlap = true;
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
}
partial void SelectProjSpecific(Character character)
{
var nonItems = itemList.Content.Children.Where(c => !(c.UserData is FabricableItem)).ToList();
nonItems.ForEach(i => itemList.Content.RemoveChild(i));
itemList.Content.RectTransform.SortChildren((c1, c2) =>
{
var item1 = c1.GUIComponent.UserData as FabricableItem;
var item2 = c2.GUIComponent.UserData as FabricableItem;
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), "Sufficient skills to fabricate:", textColor: Color.LightGreen)
{
CanBeFocused = false
};
sufficientSkillsText.RectTransform.SetAsFirstChild();
var insufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform), "Insufficient skills to fabricate:", textColor: Color.Orange)
{
CanBeFocused = false
};
var firstinSufficient = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricableItem 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();
FabricableItem targetItem = fabricatedItem ?? selectedItem;
if (targetItem != null)
{
int slotIndex = 0;
var missingItems = new List<FabricableItem.RequiredItem>();
foreach (FabricableItem.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 (FabricableItem.RequiredItem requiredItem in missingItems)
{
//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);
if (item.ParentInventory.slots[availableSlotIndex].HighlightTimer <= 0.0f)
{
item.ParentInventory.slots[availableSlotIndex].ShowBorderHighlight(Color.LightGreen * 0.5f, 0.5f, 0.5f);
}
}
}
while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.Items[slotIndex] != null)
{
slotIndex++;
}
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();
FabricableItem targetItem = fabricatedItem ?? selectedItem;
if (targetItem != null)
{
var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.sprite;
Rectangle slotRect = outputContainer.Inventory.slots[0].Rect;
GUI.DrawRectangle(spriteBatch,
new Rectangle(
slotRect.X, slotRect.Y + (int)(slotRect.Height * (1.0f - progressState)),
slotRect.Width, (int)(slotRect.Height * progressState)),
Color.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 SelectItem(GUIComponent component, object obj)
{
FabricableItem targetItem = obj as FabricableItem;
if (targetItem == null) return false;
selectedItem = obj as FabricableItem;
if (selectedItem == null) return false;
if (selectedItemFrame != null) GuiFrame.RemoveChild(selectedItemFrame);
selectedItemFrame.ClearChildren();
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), selectedItemFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f, Stretch = true };
//int width = 200, height = 150;
selectedItemFrame = new GUIFrame(new Rectangle(0, 0, (int)(GuiFrame.Rect.Width * 0.4f), 350), Color.Black * 0.8f, Alignment.CenterY | Alignment.Right, null, GuiFrame);
selectedItemFrame.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f);
progressBar = new GUIProgressBar(new Rectangle(0, 0, 0, 20), Color.Green, "", 0.0f, Alignment.BottomCenter, selectedItemFrame);
progressBar.IsHorizontal = true;
if (targetItem.TargetItem.sprite != null)
/*var itemIcon = selectedItem.TargetItem.InventoryIcon ?? selectedItem.TargetItem.sprite;
if (itemIcon != null)
{
int y = 0;
GUIImage img = new GUIImage(new Rectangle(10, 0, 40, 40), targetItem.TargetItem.sprite, Alignment.TopLeft, selectedItemFrame);
img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
img.Color = targetItem.TargetItem.SpriteColor;
new GUITextBlock(
new Rectangle(60, 0, 0, 25),
targetItem.TargetItem.Name,
Color.Transparent, Color.White,
Alignment.TopLeft,
Alignment.TopLeft, null,
selectedItemFrame, true);
y += 40;
if (!string.IsNullOrWhiteSpace(targetItem.TargetItem.Description))
GUIImage img = new GUIImage(new RectTransform(new Point(40, 40), paddedFrame.RectTransform),
itemIcon, scaleToFit: true)
{
var description = new GUITextBlock(
new Rectangle(0, y, 0, 0),
targetItem.TargetItem.Description,
"", Alignment.TopLeft, Alignment.TopLeft,
selectedItemFrame, true, GUI.SmallFont);
Color = selectedItem.TargetItem.InventoryIconColor
};
}*/
var nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
selectedItem.TargetItem.Name, textAlignment: Alignment.CenterLeft);
y += description.Rect.Height + 10;
}
List<Skill> inadequateSkills = new List<Skill>();
if (Character.Controlled != null)
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);
if (description.Rect.Height > paddedFrame.Rect.Height * 0.4f)
{
inadequateSkills = targetItem.RequiredSkills.FindAll(skill => Character.Controlled.GetSkillLevel(skill.Name) < skill.Level);
description.Wrap = false;
description.Text = description.WrappedText.Split('\n').First()+"...";
nameBlock.ToolTip = description.ToolTip = selectedItem.TargetItem.Description;
description.RectTransform.MaxSize = new Point(int.MaxValue, (int)description.Font.MeasureString(description.Text).Y);
}
string text = TextManager.Get("FabricatorRequiredItems")+ ":\n";
foreach (Tuple<ItemPrefab, int, float, bool> ip in targetItem.RequiredItems)
}
List<Skill> inadequateSkills = new List<Skill>();
if (Character.Controlled != null)
{
inadequateSkills = selectedItem.RequiredSkills.FindAll(skill => Character.Controlled.GetSkillLevel(skill.Identifier) < skill.Level);
}
if (selectedItem.RequiredSkills.Any())
{
string text = TextManager.Get("FabricatorRequiredSkills") + ":\n";
foreach (Skill skill in selectedItem.RequiredSkills)
{
text += " - " + ip.Item1.Name + " x" + ip.Item2 + (ip.Item3 < 1.0f ? ", " + ip.Item3 * 100 + "% " + TextManager.Get("FabricatorRequiredCondition") + "\n" : "\n");
text += " - " + TextManager.Get("SkillName." + skill.Identifier) + " " + TextManager.Get("Lvl").ToLower() + " " + skill.Level;
if (skill != selectedItem.RequiredSkills.Last()) { text += "\n"; }
}
text += TextManager.Get("FabricatorRequiredTime") + ": " + targetItem.RequiredTime + " s\n";
var requiredItemsText = new GUITextBlock(
new Rectangle(0, y, 0, 0),
text,
Color.Transparent, Color.White,
Alignment.TopLeft,
Alignment.TopLeft, null,
selectedItemFrame,
font: GUI.SmallFont);
if (targetItem.RequiredSkills.Any())
{
text = TextManager.Get("FabricatorRequiredSkills") + ":\n";
foreach (Skill skill in inadequateSkills)
{
text += " - " + skill.Name + " " + TextManager.Get("Lvl").ToLower() + " " + skill.Level + "\n";
}
new GUITextBlock(
new Rectangle(0, y + requiredItemsText.Rect.Height, 0, 0),
text,
Color.Transparent, inadequateSkills.Any() ? Color.Red : Color.White,
Alignment.TopLeft,
Alignment.TopLeft, null,
selectedItemFrame,
font: GUI.SmallFont);
}
activateButton = new GUIButton(new Rectangle(0, -30, 100, 20), TextManager.Get("FabricatorCreate"), Color.White, Alignment.CenterX | Alignment.Bottom, "", selectedItemFrame);
activateButton.OnClicked = StartButtonClicked;
activateButton.UserData = targetItem;
activateButton.Enabled = false;
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform), text,
textColor: inadequateSkills.Any() ? Color.Red : Color.LightGreen, font: GUI.SmallFont);
}
float degreeOfSuccess = DegreeOfSuccess(Character.Controlled, selectedItem.RequiredSkills);
if (degreeOfSuccess > 0.5f) { degreeOfSuccess = 1.0f; }
float requiredTime = GetRequiredTime(selectedItem, Character.Controlled);
string requiredTimeText = TextManager.Get("FabricatorRequiredTime") + ": " + ToolBox.SecondsToReadableTime(requiredTime);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
requiredTimeText, textColor: ToolBox.GradientLerp(degreeOfSuccess, Color.Red, Color.Yellow, Color.LightGreen), font: GUI.SmallFont);
return true;
}
private bool StartButtonClicked(GUIButton button, object obj)
{
if (selectedItem == null) { return false; }
if (fabricatedItem == null)
{
StartFabricating(obj as FabricableItem, Character.Controlled);
StartFabricating(selectedItem, Character.Controlled);
}
else
{
@@ -173,49 +335,29 @@ namespace Barotrauma.Items.Components
return true;
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
GuiFrame.Draw(spriteBatch);
}
public override void AddToGUIUpdateList()
{
GuiFrame.AddToGUIUpdateList();
}
public override void UpdateHUD(Character character)
{
FabricableItem targetItem = itemList.SelectedData as FabricableItem;
if (targetItem != null)
{
activateButton.Enabled = CanBeFabricated(targetItem, character);
}
activateButton.Enabled = false;
inSufficientPowerWarning.Visible = powerConsumption > 0 && voltage < minVoltage;
var availableIngredients = GetAvailableIngredients();
if (character != null)
{
bool itemsChanged = false;
if (prevContainedItems == null)
foreach (GUIComponent child in itemList.Content.Children)
{
itemsChanged = true;
}
else
{
var itemContainer = item.GetComponent<ItemContainer>();
for (int i = 0; i < itemContainer.Inventory.Items.Length; i++)
var itemPrefab = child.UserData as FabricableItem;
if (itemPrefab == null) continue;
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients);
if (itemPrefab == selectedItem)
{
if (prevContainedItems[i] != itemContainer.Inventory.Items[i])
{
itemsChanged = true;
break;
}
activateButton.Enabled = canBeFabricated;
}
child.GetChild<GUITextBlock>().TextColor = Color.White * (canBeFabricated ? 1.0f : 0.5f);
child.GetChild<GUIImage>().Color = itemPrefab.TargetItem.InventoryIconColor * (canBeFabricated ? 1.0f : 0.5f);
}
if (itemsChanged) CheckFabricableItems(character);
}
GuiFrame.Update((float)Timing.Step);
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
@@ -227,8 +369,10 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1);
UInt16 userID = msg.ReadUInt16();
Character user = Entity.FindEntityByID(userID) as Character;
if (itemIndex == -1)
if (itemIndex == -1 || user == null)
{
CancelFabricating();
}
@@ -239,7 +383,7 @@ namespace Barotrauma.Items.Components
if (itemIndex < 0 || itemIndex >= fabricableItems.Count) return;
SelectItem(null, fabricableItems[itemIndex]);
StartFabricating(fabricableItems[itemIndex]);
StartFabricating(fabricableItems[itemIndex], user);
}
}
}
@@ -1,126 +1,225 @@
using Microsoft.Xna.Framework;
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
{
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
private GUIFrame submarineContainer;
private GUIFrame hullInfoFrame;
private GUITextBlock hullNameText, hullBreachText, hullAirQualityText, hullWaterText;
private List<Submarine> displayedSubs = new List<Submarine>();
partial void InitProjSpecific(XElement element)
{
new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center),
DrawHUDBack, null);
submarineContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), style: null);
hullInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.13f), GUI.Canvas, minSize: new Point(250, 150)),
style: "InnerFrame")
{
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), "");
hullBreachText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), hullInfoContainer.RectTransform), "");
hullAirQualityText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), hullInfoContainer.RectTransform), "");
hullWaterText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), hullInfoContainer.RectTransform), "");
hullInfoFrame.Children.ForEach(c => { c.CanBeFocused = false; c.Children.ForEach(c2 => c2.CanBeFocused = false); });
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
if (hasPower) hullInfoFrame.AddToGUIUpdateList();
}
public override void OnMapLoaded()
{
base.OnMapLoaded();
CreateHUD();
}
private void CreateHUD()
{
submarineContainer.ClearChildren();
if (item.Submarine == null) return;
int width = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
int x = GuiFrame.Rect.X;
int y = GuiFrame.Rect.Y;
item.Submarine.CreateMiniMap(submarineContainer);
displayedSubs.Clear();
displayedSubs.Add(item.Submarine);
displayedSubs.AddRange(item.Submarine.DockedTo);
}
GuiFrame.Draw(spriteBatch);
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();
}
if (!hasPower) return;
float distort = 1.0f - item.Condition / 100.0f;
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);
}
}
}
Rectangle miniMap = new Rectangle(x + 20, y + 40, width - 40, height - 60);
private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container)
{
hullInfoFrame.Visible = false;
if (item.Submarine == null || !hasPower)
{
foreach (Hull hull in Hull.hullList)
{
var hullFrame = submarineContainer.Children.First().FindChild(hull);
if (hullFrame == null) continue;
float size = Math.Min((float)miniMap.Width / (float)item.Submarine.Borders.Width, (float)miniMap.Height / (float)item.Submarine.Borders.Height);
hullFrame.Color = Color.DarkCyan * 0.3f;
hullFrame.Children.First().Color = Color.DarkCyan * 0.3f;
}
}
float scale = 1.0f;
HashSet<Submarine> subs = new HashSet<Submarine>();
foreach (Hull hull in Hull.hullList)
{
Point topLeft = new Point(
miniMap.X + (int)((hull.Rect.X - item.Submarine.HiddenSubPosition.X - item.Submarine.Borders.X) * size),
miniMap.Y - (int)((hull.Rect.Y - item.Submarine.HiddenSubPosition.Y - item.Submarine.Borders.Y) * size));
if (hull.Submarine == null) continue;
var hullFrame = submarineContainer.Children.First().FindChild(hull);
if (hullFrame == null) continue;
Point bottomRight = new Point(
topLeft.X + (int)(hull.Rect.Width * size),
topLeft.Y + (int)(hull.Rect.Height * size));
topLeft.X = (int)MathUtils.RoundTowardsClosest(topLeft.X, 4);
topLeft.Y = (int)MathUtils.RoundTowardsClosest(topLeft.Y, 4);
bottomRight.X = (int)MathUtils.RoundTowardsClosest(bottomRight.X, 4);
bottomRight.Y = (int)MathUtils.RoundTowardsClosest(bottomRight.Y, 4);
Rectangle hullRect = new Rectangle(
topLeft, bottomRight - topLeft);
HullData hullData;
hullDatas.TryGetValue(hull, out hullData);
Color borderColor = Color.Green;
//hull integrity -----------------------------------
hullDatas.TryGetValue(hull, out HullData hullData);
if (hullData == null)
{
hullData = new HullData();
hullDatas.Add(hull, hullData);
}
if (hullData.Distort)
{
hullFrame.Children.First().Color = Color.Lerp(Color.Black, Color.DarkGray * 0.5f, Rand.Range(0.0f, 1.0f));
hullFrame.Color = Color.DarkGray * 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 = Color.DarkCyan;
float? gapOpenSum = 0.0f;
if (ShowHullIntegrity)
{
gapOpenSum = hull.ConnectedGaps.Where(g => !g.IsRoomToRoom).Sum(g => g.Open);
borderColor = Color.Lerp(borderColor, Color.Red, Math.Min((float)gapOpenSum, 1.0f));
borderColor = Color.Lerp(Color.DarkCyan, Color.Red, Math.Min((float)gapOpenSum, 1.0f));
}
//oxygen -----------------------------------
float? oxygenAmount = null;
if (RequireOxygenDetectors && (hullData == null || hullData.Oxygen == null))
if (!RequireOxygenDetectors || hullData?.Oxygen != null)
{
borderColor *= 0.5f;
oxygenAmount = RequireOxygenDetectors ? hullData.Oxygen : hull.OxygenPercentage;
GUI.DrawRectangle(spriteBatch, hullFrame.Rect, Color.Lerp(Color.Red * 0.5f, Color.Green * 0.3f, (float)oxygenAmount / 100.0f), true);
}
else
{
oxygenAmount = hullData != null && hullData.Oxygen != null ? (float)hullData.Oxygen : hull.OxygenPercentage;
GUI.DrawRectangle(spriteBatch, hullRect, Color.Lerp(Color.Red * 0.5f, Color.Green * 0.3f, (float)oxygenAmount / 100.0f), true);
}
//water -----------------------------------
float? waterAmount = null;
if (RequireWaterDetectors && (hullData == null || hullData.Water == null))
if (!RequireWaterDetectors || hullData.Water != null)
{
borderColor *= 0.5f;
}
else
{
waterAmount = hullData != null && hullData.Water != null ?
(float)hullData.Water :
Math.Min(hull.WaterVolume / hull.Volume, 1.0f);
if (hullRect.Height * waterAmount > 3.0f)
waterAmount = RequireWaterDetectors ? hullData.Water : Math.Min(hull.WaterVolume / hull.Volume, 1.0f);
if (hullFrame.Rect.Height * waterAmount > 3.0f)
{
Rectangle waterRect = new Rectangle(
hullRect.X,
(int)(hullRect.Y + hullRect.Height * (1.0f - waterAmount)),
hullRect.Width,
(int)(hullRect.Height * waterAmount));
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, Color.DarkBlue, true);
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 (hullRect.Contains(PlayerInput.MousePosition))
if (GUI.MouseOn == hullFrame || hullFrame.IsParentOf(GUI.MouseOn))
{
borderColor = Color.White;
hullInfoFrame.RectTransform.ScreenSpaceOffset = hullFrame.Rect.Center;
if (gapOpenSum > 0.1f)
{
GUI.DrawString(spriteBatch,
new Vector2(x + 10, y + height - 60),
TextManager.Get("MiniMapHullBreach"), Color.Red, Color.Black * 0.5f, 2, GUI.SmallFont);
}
hullInfoFrame.Visible = true;
hullNameText.Text = hull.RoomName;
GUI.DrawString(spriteBatch,
new Vector2(x + 10, y + height - 60),
oxygenAmount == null ? TextManager.Get("MiniMapAirQualityUnavailable") : TextManager.Get("MiniMapAirQuality") + ": " + (int)oxygenAmount + " %",
oxygenAmount == null ? Color.Red : Color.Lerp(Color.Red, Color.LightGreen, (float)oxygenAmount / 100.0f),
Color.Black * 0.5f, 2, GUI.SmallFont);
hullBreachText.Text = gapOpenSum > 0.1f ? TextManager.Get("MiniMapHullBreach") : "";
hullBreachText.TextColor = Color.Red;
GUI.DrawString(spriteBatch,
new Vector2(x + 10, y + height - 40),
waterAmount == null ? TextManager.Get("MiniMapWaterLevelUnavailable") : TextManager.Get("MiniMapWaterLevel") + ": " + (int)(waterAmount * 100.0f) + " %",
waterAmount == null ? Color.Red : Color.Lerp(Color.LightGreen, Color.Red, (float)waterAmount),
Color.Black * 0.5f, 2, GUI.SmallFont);
hullAirQualityText.Text = oxygenAmount == null ? TextManager.Get("MiniMapAirQualityUnavailable") : TextManager.Get("MiniMapAirQuality") + ": " + (int)oxygenAmount + " %";
hullAirQualityText.TextColor = oxygenAmount == null ? Color.Red : Color.Lerp(Color.Red, Color.LightGreen, (float)oxygenAmount / 100.0f);
hullWaterText.Text = waterAmount == null ? TextManager.Get("MiniMapWaterLevelUnavailable") : TextManager.Get("MiniMapWaterLevel") + ": " + (int)(waterAmount * 100.0f) + " %";
hullWaterText.TextColor = waterAmount == null ? Color.Red : Color.Lerp(Color.LightGreen, Color.Red, (float)waterAmount);
borderColor = Color.Lerp(borderColor, Color.White, 0.5f);
hullFrame.Children.First().Color = Color.White;
}
else
{
hullFrame.Children.First().Color = Color.DarkCyan * 0.8f;
}
hullFrame.Color = borderColor;
}
GUI.DrawRectangle(spriteBatch, hullRect, borderColor, false, 0.0f, 2);
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: 10);
}
}
}
}
@@ -1,20 +1,62 @@
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
{
private GUITickBox isActiveTickBox;
private GUIScrollBar isActiveSlider;
private GUIScrollBar pumpSpeedSlider;
private GUITickBox powerIndicator;
partial void InitProjSpecific()
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)
{
isActiveTickBox = new GUITickBox(new Rectangle(0, 0, 20, 20), TextManager.Get("PumpRunning"), Alignment.TopLeft, GuiFrame);
isActiveTickBox.OnSelected = (GUITickBox box) =>
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.9f, 0.8f), GuiFrame.RectTransform, Anchor.Center), style: null);
isActiveSlider = new GUIScrollBar(new RectTransform(new Point(50, 100), paddedFrame.RectTransform, Anchor.CenterLeft),
barSize: 0.2f, style: "OnOffLever")
{
IsBooleanSwitch = true,
MinValue = 0.25f,
MaxValue = 0.75f
};
var sliderHandle = isActiveSlider.GetChild<GUIButton>();
sliderHandle.RectTransform.NonScaledSize = new Point(84, sliderHandle.Rect.Height);
isActiveSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
bool active = scrollBar.BarScroll < 0.5f;
if (active == IsActive) return false;
targetLevel = null;
IsActive = !IsActive;
IsActive = active;
if (!IsActive) currPowerConsumption = 0.0f;
if (GameMain.Server != null)
@@ -31,66 +73,102 @@ namespace Barotrauma.Items.Components
return true;
};
var button = new GUIButton(new Rectangle(160, 40, 35, 30), TextManager.Get("PumpOut"), "", GuiFrame);
button.OnClicked = (GUIButton btn, object obj) =>
var rightArea = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 1.0f), paddedFrame.RectTransform, Anchor.CenterRight)) { RelativeSpacing = 0.1f };
powerIndicator = new GUITickBox(new RectTransform(new Point(30, 30), rightArea.RectTransform), TextManager.Get("PumpPowered"), style: "IndicatorLightGreen")
{
FlowPercentage -= 10.0f;
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
GameServer.Log(Character.Controlled.LogName + " set the pumping speed of " + item.Name + " to " + (int)(flowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
}
else if (GameMain.Client != null)
{
correctionTimer = CorrectionDelay;
item.CreateClientEvent(this);
}
return true;
CanBeFocused = false
};
button = new GUIButton(new Rectangle(210, 40, 35, 30), TextManager.Get("PumpIn"), "", GuiFrame);
button.OnClicked = (GUIButton btn, object obj) =>
var pumpSpeedText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), rightArea.RectTransform) { RelativeOffset = new Vector2(0.25f, 0.0f) },
"", textAlignment: Alignment.BottomLeft);
string pumpSpeedStr = TextManager.Get("PumpSpeed");
pumpSpeedText.TextGetter = () => { return pumpSpeedStr + ": " + (int)flowPercentage + " %"; };
var sliderArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), rightArea.RectTransform, Anchor.CenterLeft), isHorizontal: true)
{
FlowPercentage += 10.0f;
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
GameServer.Log(Character.Controlled.LogName + " set the pumping speed of " + item.Name + " to " + (int)(flowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
}
else if (GameMain.Client != null)
{
correctionTimer = CorrectionDelay;
item.CreateClientEvent(this);
}
return true;
Stretch = true,
RelativeSpacing = 0.05f
};
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), sliderArea.RectTransform),
TextManager.Get("PumpOut"), textAlignment: Alignment.Center);
pumpSpeedSlider = new GUIScrollBar(new RectTransform(new Vector2(0.8f, 1.0f), sliderArea.RectTransform), barSize: 0.25f, style: "GUISlider")
{
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.Server != null)
{
item.CreateServerEvent(this);
GameServer.Log(Character.Controlled.LogName + " set the pumping speed of " + item.Name + " to " + (int)(flowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
}
else if (GameMain.Client != null)
{
correctionTimer = CorrectionDelay;
item.CreateClientEvent(this);
}
return true;
}
};
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), sliderArea.RectTransform),
TextManager.Get("PumpIn"), textAlignment: Alignment.Center);
}
public override void OnItemLoaded()
{
if (pumpSpeedSlider != null)
{
pumpSpeedSlider.BarScroll = (flowPercentage + 100.0f) / 200.0f;
}
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
partial void UpdateProjSpecific(float deltaTime)
{
int x = GuiFrame.Rect.X;
int y = GuiFrame.Rect.Y;
GuiFrame.Draw(spriteBatch);
GUI.Font.DrawString(spriteBatch, TextManager.Get("PumpSpeed") + ": " + (int)flowPercentage + " %", new Vector2(x + 40, y + 85), Color.White);
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));
}
}
}
public override void AddToGUIUpdateList()
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
GuiFrame.AddToGUIUpdateList();
}
powerIndicator.Selected = hasPower && IsActive;
public override void UpdateHUD(Character character)
{
GuiFrame.Update(1.0f / 60.0f);
}
if (!PlayerInput.LeftButtonHeld())
{
isActiveSlider.BarScroll += (IsActive ? -10.0f : 10.0f) * deltaTime;
float pumpSpeedScroll = (FlowPercentage + 100.0f) / 200.0f;
if (Math.Abs(pumpSpeedScroll - pumpSpeedSlider.BarScroll) > 0.01f)
{
pumpSpeedSlider.BarScroll = pumpSpeedScroll;
}
}
}
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
{
//flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this
@@ -1,468 +0,0 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using Voronoi2;
namespace Barotrauma.Items.Components
{
partial class Radar : Powered, IServerSerializable, IClientSerializable
{
private GUITickBox isActiveTickBox;
private List<RadarBlip> radarBlips;
private static Color[] blipColorGradient =
{
Color.TransparentBlack,
new Color(0, 50, 160),
new Color(0, 133, 166),
new Color(2, 159, 30),
new Color(255, 255, 255)
};
public override void AddToGUIUpdateList()
{
GuiFrame.AddToGUIUpdateList();
}
public override void UpdateHUD(Character character)
{
GuiFrame.Update((float)Timing.Step);
for (int i = radarBlips.Count - 1; i >= 0; i--)
{
radarBlips[i].FadeTimer -= (float)Timing.Step * 0.5f;
if (radarBlips[i].FadeTimer <= 0.0f) radarBlips.RemoveAt(i);
}
if (IsActive)
{
float pingRadius = displayRadius * pingState;
Ping(item.WorldPosition, pingRadius, prevPingRadius, displayScale, range, 2.0f);
prevPingRadius = pingRadius;
return;
}
float passivePingRadius = (float)Math.Sin(Timing.TotalTime * 10);
if (passivePingRadius > 0.0f)
{
foreach (AITarget t in AITarget.List)
{
if (t.SoundRange <= 0.0f) continue;
if (Vector2.Distance(t.WorldPosition, item.WorldPosition) < t.SoundRange)
{
Ping(t.WorldPosition, t.SoundRange * passivePingRadius * 0.2f, t.SoundRange * prevPassivePingRadius * 0.2f, displayScale, t.SoundRange, 0.5f);
radarBlips.Add(new RadarBlip(t.WorldPosition, 1.0f, 1.0f));
}
}
}
prevPassivePingRadius = passivePingRadius;
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
GuiFrame.Draw(spriteBatch);
int radius = GuiFrame.Rect.Height / 2 - 10;
DrawRadar(spriteBatch, new Rectangle((int)GuiFrame.Center.X - radius, (int)GuiFrame.Center.Y - radius, radius * 2, radius * 2));
}
private void DrawRadar(SpriteBatch spriteBatch, Rectangle rect)
{
center = new Vector2(rect.X + rect.Width * 0.5f, rect.Center.Y);
displayRadius = (rect.Width / 2.0f) * (1.0f - displayBorderSize);
displayScale = displayRadius / range;
if (IsActive)
{
pingCircle.Draw(spriteBatch, center, Color.White * (1.0f - pingState), 0.0f, (displayRadius * 2 / pingCircle.size.X) * pingState);
}
if (item.Submarine != null && !DetectSubmarineWalls)
{
float simScale = displayScale * Physics.DisplayToSimRation;
foreach (Submarine submarine in Submarine.Loaded)
{
if (submarine != item.Submarine && !submarine.DockedTo.Contains(item.Submarine)) continue;
if (submarine.HullVertices == null) continue;
Vector2 offset = ConvertUnits.ToSimUnits(submarine.WorldPosition - item.WorldPosition);
for (int i = 0; i < submarine.HullVertices.Count; i++)
{
Vector2 start = (submarine.HullVertices[i] + offset) * simScale;
start.Y = -start.Y;
Vector2 end = (submarine.HullVertices[(i + 1) % submarine.HullVertices.Count] + offset) * simScale;
end.Y = -end.Y;
GUI.DrawLine(spriteBatch, center + start, center + end, Color.LightBlue);
}
}
}
if (radarBlips.Count > 0)
{
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
foreach (RadarBlip radarBlip in radarBlips)
{
DrawBlip(spriteBatch, radarBlip, center, radarBlip.FadeTimer / 2.0f);
}
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, GameMain.ScissorTestEnable);
}
if (GameMain.DebugDraw)
{
GUI.DrawString(spriteBatch, rect.Location.ToVector2(), radarBlips.Count.ToString(), Color.White);
}
if (screenOverlay != null)
{
screenOverlay.Draw(spriteBatch, center, 0.0f, rect.Width / screenOverlay.size.X);
}
if (GameMain.GameSession == null) return;
DrawMarker(spriteBatch,
GameMain.GameSession.StartLocation.Name,
(Level.Loaded.StartPosition - item.WorldPosition), displayScale, center, (rect.Width * 0.5f));
DrawMarker(spriteBatch,
GameMain.GameSession.EndLocation.Name,
(Level.Loaded.EndPosition - item.WorldPosition), displayScale, center, (rect.Width * 0.5f));
if (GameMain.GameSession.Mission != null)
{
var mission = GameMain.GameSession.Mission;
if (!string.IsNullOrWhiteSpace(mission.RadarLabel) && mission.RadarPosition != Vector2.Zero)
{
DrawMarker(spriteBatch,
mission.RadarLabel,
mission.RadarPosition - item.WorldPosition, displayScale, center, (rect.Width * 0.55f));
}
}
foreach (Submarine sub in Submarine.Loaded)
{
if (!sub.OnRadar) continue;
if (item.Submarine == sub || sub.DockedTo.Contains(item.Submarine)) continue;
if (sub.WorldPosition.Y > Level.Loaded.Size.Y) continue;
DrawMarker(spriteBatch, sub.Name, sub.WorldPosition - item.WorldPosition, displayScale, center, (rect.Width * 0.45f));
}
if (!GameMain.DebugDraw) return;
var steering = item.GetComponent<Steering>();
if (steering == null || steering.SteeringPath == null) return;
Vector2 prevPos = Vector2.Zero;
foreach (WayPoint wp in steering.SteeringPath.Nodes)
{
Vector2 pos = (wp.Position - item.WorldPosition) * 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), (steering.SteeringPath.CurrentNode == wp) ? Color.LightGreen : Color.Green, false);
if (prevPos != Vector2.Zero)
{
GUI.DrawLine(spriteBatch, pos, prevPos, Color.Green);
}
prevPos = pos;
}
}
private void Ping(Vector2 pingSource, float pingRadius, float prevPingRadius, float displayScale, float range, float pingStrength = 1.0f)
{
//inside a hull -> only show the edges of the hull
if (item.CurrentHull != null && DetectSubmarineWalls)
{
CreateBlipsForLine(
new Vector2(item.CurrentHull.WorldRect.X, item.CurrentHull.WorldRect.Y),
new Vector2(item.CurrentHull.WorldRect.Right, item.CurrentHull.WorldRect.Y),
pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f);
CreateBlipsForLine(
new Vector2(item.CurrentHull.WorldRect.X, item.CurrentHull.WorldRect.Y - item.CurrentHull.Rect.Height),
new Vector2(item.CurrentHull.WorldRect.Right, item.CurrentHull.WorldRect.Y - item.CurrentHull.Rect.Height),
pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f);
CreateBlipsForLine(
new Vector2(item.CurrentHull.WorldRect.X, item.CurrentHull.WorldRect.Y),
new Vector2(item.CurrentHull.WorldRect.X, item.CurrentHull.WorldRect.Y - item.CurrentHull.Rect.Height),
pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f);
CreateBlipsForLine(
new Vector2(item.CurrentHull.WorldRect.Right, item.CurrentHull.WorldRect.Y),
new Vector2(item.CurrentHull.WorldRect.Right, item.CurrentHull.WorldRect.Y - item.CurrentHull.Rect.Height),
pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f);
return;
}
foreach (Submarine submarine in Submarine.Loaded)
{
if (item.Submarine == submarine && !DetectSubmarineWalls) continue;
if (item.Submarine != null && item.Submarine.DockedTo.Contains(submarine)) continue;
if (submarine.HullVertices == null) continue;
for (int i = 0; i < submarine.HullVertices.Count; i++)
{
Vector2 start = ConvertUnits.ToDisplayUnits(submarine.HullVertices[i]);
Vector2 end = ConvertUnits.ToDisplayUnits(submarine.HullVertices[(i + 1) % submarine.HullVertices.Count]);
if (item.Submarine == submarine)
{
start += Rand.Vector(500.0f);
end += Rand.Vector(500.0f);
}
CreateBlipsForLine(
start + submarine.WorldPosition,
end + submarine.WorldPosition,
pingRadius, prevPingRadius,
200.0f, 2.0f, range, 1.0f);
}
}
if (Level.Loaded != null && (item.CurrentHull == null || !DetectSubmarineWalls))
{
if (Level.Loaded.Size.Y - pingSource.Y < range)
{
CreateBlipsForLine(
new Vector2(pingSource.X - range, Level.Loaded.Size.Y),
new Vector2(pingSource.X + range, Level.Loaded.Size.Y),
pingRadius, prevPingRadius,
250.0f, 150.0f, range, pingStrength);
}
List<VoronoiCell> cells = Level.Loaded.GetCells(pingSource, 7);
foreach (VoronoiCell cell in cells)
{
foreach (GraphEdge edge in cell.edges)
{
if (!edge.isSolid) continue;
float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center);
if (cellDot > 0) continue;
float facingDot = Vector2.Dot(
Vector2.Normalize(edge.point1 - edge.point2),
Vector2.Normalize(cell.Center - pingSource));
CreateBlipsForLine(
edge.point1 + cell.Translation,
edge.point2 + cell.Translation,
pingRadius, prevPingRadius,
350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f), range, pingStrength);
}
}
foreach (RuinGeneration.Ruin ruin in Level.Loaded.Ruins)
{
if (!MathUtils.CircleIntersectsRectangle(pingSource, range, ruin.Area)) continue;
foreach (var ruinShape in ruin.RuinShapes)
{
foreach (RuinGeneration.Line wall in ruinShape.Walls)
{
float cellDot = Vector2.Dot(
Vector2.Normalize(ruinShape.Center - pingSource),
Vector2.Normalize((wall.A + wall.B) / 2.0f - ruinShape.Center));
if (cellDot > 0) continue;
CreateBlipsForLine(
wall.A, wall.B,
pingRadius, prevPingRadius,
100.0f, 1000.0f, range, pingStrength);
}
}
}
}
foreach (Character c in Character.CharacterList)
{
if (c.AnimController.CurrentHull != null || !c.Enabled) continue;
if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) continue;
foreach (Limb limb in c.AnimController.Limbs)
{
float pointDist = (limb.WorldPosition - pingSource).Length() * displayScale;
if (limb.SimPosition == Vector2.Zero || pointDist > displayRadius) continue;
if (pointDist > prevPingRadius && pointDist < pingRadius)
{
var blip = new RadarBlip(
limb.WorldPosition + Rand.Vector(limb.Mass / 10.0f),
MathHelper.Clamp(limb.Mass, 0.1f, pingStrength),
MathHelper.Clamp(limb.Mass * 0.1f, 0.1f, 2.0f));
radarBlips.Add(blip);
}
}
}
}
private void CreateBlipsForLine(Vector2 point1, Vector2 point2, float pingRadius, float prevPingRadius,
float lineStep, float zStep, float range, float pingStrength)
{
float length = (point1 - point2).Length();
Vector2 lineDir = (point2 - point1) / length;
range *= displayScale;
for (float x = 0; x < length; x += lineStep * Rand.Range(0.8f, 1.2f))
{
Vector2 point = point1 + lineDir * x;
//point += cell.Translation;
float pointDist = Vector2.Distance(item.WorldPosition, point) * displayScale;
if (pointDist > displayRadius) continue;
if (pointDist < prevPingRadius || pointDist > pingRadius) continue;
float alpha = pingStrength * Rand.Range(1.5f, 2.0f);
for (float z = 0; z < displayRadius - pointDist * displayScale; z += zStep)
{
Vector2 pos = point + Rand.Vector(150.0f) + Vector2.Normalize(point - item.WorldPosition) * z / displayScale;
float fadeTimer = alpha * (1.0f - pointDist / range);
int minDist = 200;
radarBlips.RemoveAll(b => b.FadeTimer < fadeTimer && Math.Abs(pos.X - b.Position.X) < minDist && Math.Abs(pos.Y - b.Position.Y) < minDist);
var blip = new RadarBlip(pos, fadeTimer, 1.0f + ((pointDist+z) / displayRadius));
radarBlips.Add(blip);
zStep += 0.5f;
if (z == 0)
{
alpha = Math.Min(alpha - 0.5f, 1.5f);
}
else
{
alpha -= 0.1f;
}
if (alpha < 0) break;
}
}
}
private void DrawBlip(SpriteBatch spriteBatch, RadarBlip blip, Vector2 center, float strength)
{
strength = MathHelper.Clamp(strength, 0.0f, 1.0f);
float scaledT = strength * (blipColorGradient.Length - 1);
Color color = Color.Lerp(blipColorGradient[(int)scaledT], blipColorGradient[(int)Math.Min(scaledT + 1, blipColorGradient.Length - 1)], (scaledT - (int)scaledT));
Vector2 pos = (blip.Position - item.WorldPosition) * displayScale;
pos.Y = -pos.Y;
float posDist = pos.Length();
if (posDist > displayRadius)
{
blip.FadeTimer = 0.0f;
return;
}
if (radarBlip == null)
{
GUI.DrawRectangle(spriteBatch, center + pos, Vector2.One * 4, Color.Magenta, true);
return;
}
Vector2 dir = pos / posDist;
Vector2 normal = new Vector2(dir.Y, -dir.X);
float scale = (strength + 3.0f) * blip.Scale;
radarBlip.Draw(spriteBatch, center + pos, color, radarBlip.Origin, MathUtils.VectorToAngle(pos),
new Vector2(scale * 0.5f, scale) * 0.04f, SpriteEffects.None, 0);
pos += Rand.Range(0.0f, 1.0f) * dir + Rand.Range(-scale, scale) * normal;
radarBlip.Draw(spriteBatch, center + pos, color * 0.5f, radarBlip.Origin, 0, scale * 0.08f, SpriteEffects.None, 0);
}
private void DrawMarker(SpriteBatch spriteBatch, string label, Vector2 position, float scale, Vector2 center, float radius)
{
float dist = position.Length();
position *= scale;
position.Y = -position.Y;
float textAlpha = MathHelper.Clamp(1.5f - dist / 50000.0f, 0.5f, 1.0f);
Vector2 dir = Vector2.Normalize(position);
Vector2 markerPos = (dist * scale > radius) ? dir * radius : position;
markerPos += center;
markerPos.X = (int)markerPos.X;
markerPos.Y = (int)markerPos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X, (int)markerPos.Y, 5, 5), Color.LightBlue);
if (dir.X < 0.0f) markerPos.X -= GUI.SmallFont.MeasureString(label).X + 10;
string wrappedLabel = ToolBox.WrapText(label, 150, GUI.SmallFont);
wrappedLabel += "\n" + ((int)(dist * Physics.DisplayToRealWorldRatio) + " m");
GUI.DrawString(spriteBatch,
new Vector2(markerPos.X + 10, markerPos.Y),
wrappedLabel,
Color.LightBlue * textAlpha, Color.Black * textAlpha * 0.5f,
2, GUI.SmallFont);
}
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
{
msg.Write(IsActive);
}
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
{
if (correctionTimer > 0.0f)
{
StartDelayedCorrection(type, msg.ExtractBits(1), sendingTime);
return;
}
IsActive = msg.ReadBoolean();
isActiveTickBox.Selected = IsActive;
}
}
class RadarBlip
{
public float FadeTimer;
public Vector2 Position;
public float Scale;
public RadarBlip(Vector2 pos, float fadeTimer, float scale)
{
Position = pos;
FadeTimer = Math.Max(fadeTimer, 0.0f);
Scale = scale;
}
}
}
@@ -4,200 +4,458 @@ 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, IDrawableComponent, IServerSerializable, IClientSerializable
partial class Reactor : Powered, IServerSerializable, IClientSerializable
{
private GUITickBox autoTempTickBox;
private GUIScrollBar autoTempSlider;
private GUIScrollBar onOffSwitch;
private const int GraphSize = 25;
private float graphTimer;
private int updateGraphInterval = 500;
private Sprite fissionRateMeter, turbineOutputMeter;
private Sprite meterPointer;
private Sprite sectorSprite;
private float[] fissionRateGraph = new float[GraphSize];
private float[] coolingRateGraph = new float[GraphSize];
private float[] tempGraph = new float[GraphSize];
private Sprite tempMeterFrame, tempMeterBar;
private Sprite tempRangeIndicator;
private Sprite graphLine;
private GUIScrollBar fissionRateScrollBar;
private GUIScrollBar turbineOutputScrollBar;
private float[] outputGraph = new float[GraphSize];
private float[] loadGraph = new float[GraphSize];
private GUITickBox criticalHeatWarning;
private GUITickBox lowTemperatureWarning;
private GUITickBox criticalOutputWarning;
partial void InitProjSpecific()
private GUIFrame inventoryContainer;
private GUIComponent leftHUDColumn;
private Dictionary<string, GUIButton> warningButtons = new Dictionary<string, GUIButton>();
private static string[] warningTexts = new string[]
{
var button = new GUIButton(new Rectangle(410, 70, 40, 40), "-", "", GuiFrame);
button.OnPressed = () =>
{
lastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
ShutDownTemp -= 100.0f;
"ReactorWarningLowTemp","ReactorWarningOverheating",
"ReactorWarningLowOutput", "ReactorWarningHighOutput",
"ReactorWarningLowFuel", "ReactorWarningFuelOut",
"ReactorWarningMeltdown","ReactorWarningSCRAM"
};
return false;
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "fissionratemeter":
fissionRateMeter = new Sprite(subElement);
break;
case "turbineoutputmeter":
turbineOutputMeter = new Sprite(subElement);
break;
case "meterpointer":
meterPointer = new Sprite(subElement);
break;
case "sectorsprite":
sectorSprite = new Sprite(subElement);
break;
case "tempmeterframe":
tempMeterFrame = new Sprite(subElement);
break;
case "tempmeterbar":
tempMeterBar = new Sprite(subElement);
break;
case "temprangeindicator":
tempRangeIndicator = new Sprite(subElement);
break;
case "graphline":
graphLine = new Sprite(subElement);
break;
}
}
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.85f), GuiFrame.RectTransform, Anchor.Center), isHorizontal: true)
{
RelativeSpacing = 0.015f,
Stretch = true
};
GUIFrame columnLeft = new GUIFrame(new RectTransform(new Vector2(0.2f, 1.0f), paddedFrame.RectTransform), style: null);
leftHUDColumn = columnLeft;
GUIFrame columnMid = new GUIFrame(new RectTransform(new Vector2(0.45f, 1.0f), paddedFrame.RectTransform), style: null);
GUIFrame columnRight = new GUIFrame(new RectTransform(new Vector2(0.35f, 1.0f), paddedFrame.RectTransform), style: null);
//----------------------------------------------------------
//left column
//----------------------------------------------------------
int buttonsPerRow = 2;
int spacing = 5;
int buttonWidth = columnLeft.Rect.Width / buttonsPerRow - (spacing * (buttonsPerRow - 1));
int buttonHeight = (int)(columnLeft.Rect.Height * 0.5f) / 4;
for (int i = 0; i < warningTexts.Length; i++)
{
var warningBtn = new GUIButton(new RectTransform(new Point(buttonWidth, buttonHeight), columnLeft.RectTransform)
{ AbsoluteOffset = new Point((i % buttonsPerRow) * (buttonWidth + spacing), (int)Math.Floor(i / (float)buttonsPerRow) * (buttonHeight + spacing)) },
TextManager.Get(warningTexts[i]), style: "IndicatorButton");
var btnText = warningBtn.GetChild<GUITextBlock>();
btnText.Font = GUI.SmallFont;
btnText.Wrap = true;
btnText.SetTextPos();
warningButtons.Add(warningTexts[i], warningBtn);
}
inventoryContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.45f), columnLeft.RectTransform, Anchor.BottomLeft), style: null);
//----------------------------------------------------------
//mid column
//----------------------------------------------------------
criticalHeatWarning = new GUITickBox(new RectTransform(new Point(30, 30), columnMid.RectTransform),
TextManager.Get("ReactorWarningCriticalTemp"), font: GUI.SmallFont, style: "IndicatorLightRed")
{
CanBeFocused = false
};
lowTemperatureWarning = new GUITickBox(new RectTransform(new Point(30, 30), columnMid.RectTransform) { RelativeOffset = new Vector2(0.3f, 0.0f) },
TextManager.Get("ReactorWarningCriticalLowTemp"), font: GUI.SmallFont, style: "IndicatorLightRed")
{
CanBeFocused = false
};
criticalOutputWarning = new GUITickBox(new RectTransform(new Point(30, 30), columnMid.RectTransform) { RelativeOffset = new Vector2(0.75f, 0.0f) },
TextManager.Get("ReactorWarningCriticalOutput"), font: GUI.SmallFont, style: "IndicatorLightRed")
{
CanBeFocused = false
};
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.05f), columnMid.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.25f) },
TextManager.Get("ReactorFissionRate"));
new GUICustomComponent(new RectTransform(new Vector2(0.5f, 0.5f), columnMid.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.3f) },
DrawFissionRateMeter, null)
{
ToolTip = TextManager.Get("ReactorTipFissionRate")
};
button = new GUIButton(new Rectangle(460, 70, 40, 40), "+", "", GuiFrame);
button.OnPressed = () =>
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.05f), columnMid.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.25f) },
TextManager.Get("ReactorTurbineOutput"));
new GUICustomComponent(new RectTransform(new Vector2(0.5f, 0.5f), columnMid.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.3f) },
DrawTurbineOutputMeter, null)
{
lastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
ShutDownTemp += 100.0f;
return false;
ToolTip = TextManager.Get("ReactorTipTurbineOutput")
};
autoTempTickBox = new GUITickBox(new Rectangle(410, 170, 20, 20), TextManager.Get("ReactorAutoTemp"), Alignment.TopLeft, GuiFrame);
autoTempTickBox.OnSelected = ToggleAutoTemp;
button = new GUIButton(new Rectangle(210, 290, 40, 40), "+", "", GuiFrame);
button.OnPressed = () =>
new GUITextBlock(new RectTransform(new Point(0, 20), columnMid.RectTransform, Anchor.BottomLeft) { AbsoluteOffset = new Point(0, 90) },
TextManager.Get("ReactorFissionRate"));
fissionRateScrollBar = new GUIScrollBar(new RectTransform(new Point(columnMid.Rect.Width, 30), columnMid.RectTransform, Anchor.BottomCenter) { AbsoluteOffset = new Point(0, 60) },
style: "GUISlider", barSize: 0.1f)
{
lastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
OnMoved = (GUIScrollBar bar, float scrollAmount) =>
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
FissionRate += 1.0f;
LastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
targetFissionRate = scrollAmount * 100.0f;
return false;
return false;
}
};
button = new GUIButton(new Rectangle(210, 340, 40, 40), "-", "", GuiFrame);
button.OnPressed = () =>
new GUITextBlock(new RectTransform(new Point(0, 20), columnMid.RectTransform, Anchor.BottomLeft) { AbsoluteOffset = new Point(0, 30) },
TextManager.Get("ReactorTurbineOutput"));
turbineOutputScrollBar = new GUIScrollBar(new RectTransform(new Point(columnMid.Rect.Width, 30), columnMid.RectTransform, Anchor.BottomCenter),
style: "GUISlider", barSize: 0.1f, isHorizontal: true)
{
lastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
OnMoved = (GUIScrollBar bar, float scrollAmount) =>
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
FissionRate -= 1.0f;
LastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
targetTurbineOutput = scrollAmount * 100.0f;
return false;
return false;
}
};
button = new GUIButton(new Rectangle(500, 290, 40, 40), "+", "", GuiFrame);
button.OnPressed = () =>
//----------------------------------------------------------
//right column
//----------------------------------------------------------
new GUITextBlock(new RectTransform(new Point(100, 20), columnRight.RectTransform), TextManager.Get("ReactorAutoTemp"))
{
lastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
CoolingRate += 1.0f;
return false;
ToolTip = TextManager.Get("ReactorTipAutoTemp")
};
button = new GUIButton(new Rectangle(500, 340, 40, 40), "-", "", GuiFrame);
button.OnPressed = () =>
autoTempSlider = new GUIScrollBar(new RectTransform(new Point(100, 30), columnRight.RectTransform) { AbsoluteOffset = new Point(0, 30) },
barSize: 0.5f, style: "OnOffSlider")
{
lastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
ToolTip = TextManager.Get("ReactorTipAutoTemp"),
IsBooleanSwitch = true,
BarScroll = 1.0f,
OnMoved = (scrollBar, scrollAmount) =>
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
LastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
return true;
}
unsentChanges = true;
CoolingRate -= 1.0f;
return false;
};
onOffSwitch = new GUIScrollBar(new RectTransform(new Point(50, 80), columnRight.RectTransform, Anchor.TopRight),
barSize: 0.2f, style: "OnOffLever")
{
IsBooleanSwitch = true,
MinValue = 0.25f,
MaxValue = 0.75f,
OnMoved = (scrollBar, scrollAmount) =>
{
LastUser = Character.Controlled;
if (nextServerLogWriteTime == null)
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
unsentChanges = true;
return true;
}
};
var lever = onOffSwitch.GetChild<GUIButton>();
lever.RectTransform.NonScaledSize = new Point(lever.Rect.Width + 30, lever.Rect.Height);
var graphArea = new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.5f), columnRight.RectTransform, Anchor.BottomCenter) { AbsoluteOffset = new Point(0, 30) },
DrawGraph, null);
var loadText = new GUITextBlock(new RectTransform(new Point(100, 30), graphArea.RectTransform, Anchor.TopLeft, Pivot.BottomLeft),
"Load", textColor: Color.LightBlue, textAlignment: Alignment.CenterLeft)
{
ToolTip = TextManager.Get("ReactorTipLoad")
};
string loadStr = TextManager.Get("ReactorLoad");
loadText.TextGetter += () => { return loadStr.Replace("[kw]", ((int)load).ToString()); };
var outputText = new GUITextBlock(new RectTransform(new Point(100, 30), graphArea.RectTransform, Anchor.BottomLeft, Pivot.TopLeft),
"Output", textColor: Color.LightGreen, textAlignment: Alignment.CenterLeft)
{
ToolTip = TextManager.Get("ReactorTipPower")
};
string outputStr = TextManager.Get("ReactorOutput");
outputText.TextGetter += () => { return outputStr.Replace("[kw]", ((int)-currPowerConsumption).ToString()); };
}
public override void OnItemLoaded()
{
turbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f;
fissionRateScrollBar.BarScroll = targetFissionRate / 100.0f;
var itemContainer = item.GetComponent<ItemContainer>();
if (itemContainer != null)
{
itemContainer.AllowUIOverlap = true;
itemContainer.Inventory.RectTransform = inventoryContainer.RectTransform;
}
}
private void DrawGraph(SpriteBatch spriteBatch, GUICustomComponent container)
{
Rectangle graphArea = new Rectangle(container.Rect.X + 30, container.Rect.Y, container.Rect.Width - 30, container.Rect.Height);
float maxLoad = loadGraph.Max();
float xOffset = graphTimer / updateGraphInterval;
DrawGraph(outputGraph, spriteBatch,
graphArea, Math.Max(10000.0f, maxLoad), xOffset, Color.LightGreen);
DrawGraph(loadGraph, spriteBatch,
graphArea, Math.Max(10000.0f, maxLoad), xOffset, Color.LightBlue);
tempMeterFrame.Draw(spriteBatch, new Vector2(graphArea.X - 30, graphArea.Y), Color.White, Vector2.Zero, 0.0f, new Vector2(1.0f, graphArea.Height / tempMeterFrame.size.Y));
float tempFill = temperature / 100.0f;
int barPadding = 5;
Vector2 meterBarPos = new Vector2(graphArea.X - 30 + tempMeterFrame.size.X / 2, graphArea.Bottom - tempMeterBar.size.Y);
while (meterBarPos.Y > graphArea.Bottom - graphArea.Height * tempFill)
{
float tempRatio = 1.0f - ((meterBarPos.Y - graphArea.Y) / graphArea.Height);
Color color = tempRatio < 0.5f ?
Color.Lerp(Color.Green, Color.Orange, tempRatio * 2.0f) :
Color.Lerp(Color.Orange, Color.Red, (tempRatio - 0.5f) * 2.0f);
tempMeterBar.Draw(spriteBatch, meterBarPos, color);
meterBarPos.Y -= (tempMeterBar.size.Y + barPadding);
}
if (temperature > optimalTemperature.Y)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(graphArea.X - 30, graphArea.Y),
new Vector2(tempMeterFrame.SourceRect.Width, (graphArea.Bottom - graphArea.Height * optimalTemperature.Y / 100.0f) - graphArea.Y),
Color.Red * (float)Math.Sin(Timing.TotalTime * 5.0f) * 0.7f, isFilled: true);
}
if (temperature < optimalTemperature.X)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(graphArea.X - 30, graphArea.Bottom - graphArea.Height * optimalTemperature.X / 100.0f),
new Vector2(tempMeterFrame.SourceRect.Width, graphArea.Bottom - (graphArea.Bottom - graphArea.Height * optimalTemperature.X / 100.0f)),
Color.Red * (float)Math.Sin(Timing.TotalTime * 5.0f) * 0.7f, isFilled: true);
}
tempRangeIndicator.Draw(spriteBatch, new Vector2(meterBarPos.X, graphArea.Bottom - graphArea.Height * optimalTemperature.X / 100.0f));
tempRangeIndicator.Draw(spriteBatch, new Vector2(meterBarPos.X, graphArea.Bottom - graphArea.Height * optimalTemperature.Y / 100.0f));
}
private void UpdateGraph(float deltaTime)
{
graphTimer += deltaTime * 1000.0f;
if (graphTimer > updateGraphInterval)
{
UpdateGraph(fissionRateGraph, fissionRate);
UpdateGraph(coolingRateGraph, coolingRate);
UpdateGraph(tempGraph, temperature);
UpdateGraph(outputGraph, -currPowerConsumption);
UpdateGraph(loadGraph, load);
graphTimer = 0.0f;
}
if (autoTemp)
{
fissionRateScrollBar.BarScroll = FissionRate / 100.0f;
turbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
}
}
public void Draw(SpriteBatch spriteBatch, bool editing = false)
private void DrawFissionRateMeter(SpriteBatch spriteBatch, GUICustomComponent container)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(item.Rect.X + item.Rect.Width / 2 - 6, -item.Rect.Y + 29),
new Vector2(12, 42), Color.Black);
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = container.Rect;
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
if (temperature > 0)
GUI.DrawRectangle(spriteBatch,
new Vector2(item.Rect.X + item.Rect.Width / 2 - 5, -item.Rect.Y + 30 + (40.0f * (1.0f - temperature / 10000.0f))),
new Vector2(10, 40 * (temperature / 10000.0f)), new Color(temperature / 10000.0f, 1.0f - (temperature / 10000.0f), 0.0f, 1.0f), true);
DrawMeter(spriteBatch, container.Rect,
fissionRateMeter, FissionRate, new Vector2(0.0f, 100.0f), optimalFissionRate, allowedFissionRate);
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred);
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
private void DrawTurbineOutputMeter(SpriteBatch spriteBatch, GUICustomComponent container)
{
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;
int x = GuiFrame.Rect.X;
int y = GuiFrame.Rect.Y;
GuiFrame.Draw(spriteBatch);
float xOffset = graphTimer / updateGraphInterval;
GUI.Font.DrawString(spriteBatch, TextManager.Get("ReactorOutput") + ": " + (int)temperature + " kW",
new Vector2(x + 450, y + 30), Color.Red);
GUI.Font.DrawString(spriteBatch, TextManager.Get("ReactorGridLoad") + ": " + (int)load + " kW",
new Vector2(x + 600, y + 30), Color.Yellow);
bool lightOn = Timing.TotalTime % 0.5f < 0.25f && onOffSwitch.BarScroll < 0.5f;
float maxLoad = 0.0f;
foreach (float loadVal in loadGraph)
fissionRateScrollBar.Enabled = !autoTemp;
turbineOutputScrollBar.Enabled = !autoTemp;
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 && onOffSwitch.BarScroll > 0.5f;
AutoTemp = autoTempSlider.BarScroll < 0.5f;
shutDown = onOffSwitch.BarScroll > 0.5f;
if (shutDown)
{
maxLoad = Math.Max(maxLoad, loadVal);
}
DrawGraph(tempGraph, spriteBatch,
new Rectangle(x + 30, y + 30, 400, 250), Math.Max(10000.0f, maxLoad), xOffset, Color.Red);
DrawGraph(loadGraph, spriteBatch,
new Rectangle(x + 30, y + 30, 400, 250), Math.Max(10000.0f, maxLoad), xOffset, Color.Yellow);
GUI.Font.DrawString(spriteBatch, TextManager.Get("ReactorShutdownTemp") + ": " + (int)shutDownTemp, new Vector2(x + 450, y + 80), Color.White);
y += 300;
GUI.Font.DrawString(spriteBatch, TextManager.Get("ReactorFissionRate") + ": " + (int)fissionRate + " %", new Vector2(x + 30, y), Color.White);
DrawGraph(fissionRateGraph, spriteBatch,
new Rectangle(x + 30, y + 30, 200, 100), 100.0f, xOffset, Color.Orange);
GUI.Font.DrawString(spriteBatch, TextManager.Get("ReactorCoolingRate") + ": " + (int)coolingRate + " %", new Vector2(x + 320, y), Color.White);
DrawGraph(coolingRateGraph, spriteBatch,
new Rectangle(x + 320, y + 30, 200, 100), 100.0f, xOffset, Color.LightBlue);
}
public override void AddToGUIUpdateList()
{
GuiFrame.AddToGUIUpdateList();
}
public override void UpdateHUD(Character character)
{
GuiFrame.Update(1.0f / 60.0f);
fissionRateScrollBar.BarScroll = FissionRate / 100.0f;
turbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
}
}
private bool ToggleAutoTemp(GUITickBox tickBox)
{
unsentChanges = true;
autoTemp = tickBox.Selected;
LastUser = Character.Controlled;
return true;
}
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(
(optimalRange.X - range.X) / (range.Y - range.X),
(optimalRange.Y - range.X) / (range.Y - range.X));
Vector2 allowedRangeNormalized = new Vector2(
(allowedRange.X - range.X) / (range.Y - range.X),
(allowedRange.Y - range.X) / (range.Y - range.X));
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, Color.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));
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
sectorSprite.Draw(spriteBatch, pos, Color.LightGreen, MathHelper.PiOver2, scale);
sectorSprite.Draw(spriteBatch, pos, Color.Orange, optimalSectorRad.X, scale);
sectorSprite.Draw(spriteBatch, pos, Color.Red, allowedSectorRad.X, scale);
sectorSprite.Draw(spriteBatch, pos, Color.Orange, MathHelper.Pi + optimalSectorRad.Y, scale);
sectorSprite.Draw(spriteBatch, pos, Color.Red, MathHelper.Pi + allowedSectorRad.Y, scale);
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);
meterPointer.Draw(spriteBatch, pos, valueRad, scale);
}
static void UpdateGraph<T>(IList<T> graph, T newValue)
{
for (int i = graph.Count - 1; i > 0; i--)
@@ -207,8 +465,13 @@ namespace Barotrauma.Items.Components
graph[0] = newValue;
}
static void DrawGraph(IList<float> graph, SpriteBatch spriteBatch, Rectangle rect, float maxVal, float xOffset, Color color)
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;
@@ -223,8 +486,16 @@ namespace Barotrauma.Items.Components
currX -= lineWidth;
Vector2 newPoint = new Vector2(currX, rect.Bottom - graph[i] * yScale);
GUI.DrawLine(spriteBatch, prevPoint, newPoint - new Vector2(1.0f, 0), color);
if (graphLine == 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;
}
@@ -232,16 +503,38 @@ namespace Barotrauma.Items.Components
Vector2 lastPoint = new Vector2(rect.X,
rect.Bottom - (graph[graph.Count - 1] + (graph[graph.Count - 2] - graph[graph.Count - 1]) * xOffset) * yScale);
GUI.DrawLine(spriteBatch, prevPoint, lastPoint, color);
if (graphLine == 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()
{
graphLine.Remove();
fissionRateMeter.Remove();
turbineOutputMeter.Remove();
meterPointer.Remove();
sectorSprite.Remove();
tempMeterFrame.Remove();
tempMeterBar.Remove();
tempRangeIndicator.Remove();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
msg.Write(autoTemp);
msg.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 15);
msg.WriteRangedSingle(coolingRate, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8);
msg.Write(shutDown);
msg.WriteRangedSingle(targetFissionRate, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(targetTurbineOutput, 0.0f, 100.0f, 8);
correctionTimer = CorrectionDelay;
}
@@ -250,17 +543,20 @@ namespace Barotrauma.Items.Components
{
if (correctionTimer > 0.0f)
{
StartDelayedCorrection(type, msg.ExtractBits(16 + 1 + 15 + 8 + 8), sendingTime);
StartDelayedCorrection(type, msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8), sendingTime);
return;
}
Temperature = msg.ReadRangedSingle(0.0f, 10000.0f, 16);
AutoTemp = msg.ReadBoolean();
ShutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 15);
shutDown = 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);
CoolingRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
FissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
fissionRateScrollBar.BarScroll = targetFissionRate / 100.0f;
turbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f;
onOffSwitch.BarScroll = shutDown ? Math.Max(onOffSwitch.BarScroll, 0.55f) : Math.Min(onOffSwitch.BarScroll, 0.45f);
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,176 +1,565 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma.Items.Components
{
partial class Steering : Powered, IServerSerializable, IClientSerializable
{
private GUITickBox autopilotTickBox, maintainPosTickBox;
private GUITickBox levelEndTickBox, levelStartTickBox;
private GUITickBox autopilotTickBox, manualTickBox;
private GUITickBox maintainPosTickBox, levelEndTickBox, levelStartTickBox;
private GUIFrame autoPilotControlsDisabler;
private GUIComponent steerArea;
private GUITextBlock pressureWarningText;
private GUITextBlock tipContainer;
private string noPowerTip, autoPilotMaintainPosTip, autoPilotLevelStartTip, autoPilotLevelEndTip;
private Vector2 keyboardInput = Vector2.Zero;
private float inputCumulation;
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; }
}
public bool DockingModeEnabled
{
get;
set;
}
public DockingPort DockingSource, DockingTarget;
partial void InitProjSpecific()
{
autopilotTickBox = new GUITickBox(new Rectangle(0, 25, 20, 20), TextManager.Get("SteeringAutoPilot"), Alignment.TopLeft, GuiFrame);
autopilotTickBox.OnSelected = (GUITickBox box) =>
int viewSize = (int)Math.Min(GuiFrame.Rect.Width - 150, GuiFrame.Rect.Height * 0.9f);
var controlContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.35f), GuiFrame.RectTransform, Anchor.CenterLeft)
{ MinSize = new Point(150, 0), AbsoluteOffset = new Point((int)(viewSize * 0.99f), (int)(viewSize * 0.05f)) }, "SonarFrame");
var paddedControlContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), controlContainer.RectTransform, Anchor.Center))
{
AutoPilot = box.Selected;
unsentChanges = true;
return true;
RelativeSpacing = 0.03f,
Stretch = true
};
maintainPosTickBox = new GUITickBox(new Rectangle(5, 50, 15, 15), TextManager.Get("SteeringMaintainPos"), Alignment.TopLeft, GUI.SmallFont, GuiFrame);
maintainPosTickBox.Enabled = false;
maintainPosTickBox.OnSelected = ToggleMaintainPosition;
var statusContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GuiFrame.RectTransform, Anchor.BottomLeft)
{ MinSize = new Point(150, 0), AbsoluteOffset = new Point((int)(viewSize * 0.9f), 0) }, "SonarFrame");
var paddedStatusContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), statusContainer.RectTransform, Anchor.Center))
{
RelativeSpacing = 0.03f,
Stretch = true
};
levelStartTickBox = new GUITickBox(
new Rectangle(5, 70, 15, 15),
manualTickBox = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.3f), paddedControlContainer.RectTransform),
TextManager.Get("SteeringManual"), style: "GUIRadioButton")
{
Selected = true,
OnSelected = (GUITickBox box) =>
{
AutoPilot = !box.Selected;
unsentChanges = true;
user = Character.Controlled;
return true;
}
};
autopilotTickBox = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.3f), paddedControlContainer.RectTransform),
TextManager.Get("SteeringAutoPilot"), style: "GUIRadioButton")
{
OnSelected = (GUITickBox box) =>
{
AutoPilot = box.Selected;
if (AutoPilot && MaintainPos)
{
posToMaintain = controlledSub == null ? item.WorldPosition : controlledSub.WorldPosition;
}
unsentChanges = true;
user = Character.Controlled;
return true;
}
};
GUITickBox.CreateRadioButtonGroup(new List<GUITickBox> { manualTickBox, autopilotTickBox });
var autoPilotControls = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f), paddedControlContainer.RectTransform), "InnerFrame");
var paddedAutoPilotControls = new GUILayoutGroup(new RectTransform(new Vector2(0.8f), autoPilotControls.RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.03f
};
maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), paddedAutoPilotControls.RectTransform),
TextManager.Get("SteeringMaintainPos"), font: GUI.SmallFont)
{
Enabled = false,
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;
}
};
levelStartTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), paddedAutoPilotControls.RectTransform),
GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, 20),
Alignment.TopLeft, GUI.SmallFont, GuiFrame);
levelStartTickBox.Enabled = false;
levelStartTickBox.OnSelected = SelectDestination;
font: GUI.SmallFont)
{
Enabled = false,
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 Rectangle(5, 90, 15, 15),
levelEndTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), paddedAutoPilotControls.RectTransform),
GameMain.GameSession?.EndLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, 20),
Alignment.TopLeft, GUI.SmallFont, GuiFrame);
levelEndTickBox.Enabled = false;
levelEndTickBox.OnSelected = SelectDestination;
font: GUI.SmallFont)
{
Enabled = false,
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;
}
};
autoPilotControlsDisabler = new GUIFrame(new RectTransform(Vector2.One, autoPilotControls.RectTransform), "InnerFrame");
GUITickBox.CreateRadioButtonGroup(new List<GUITickBox> { maintainPosTickBox, levelStartTickBox, levelEndTickBox });
string steeringVelX = TextManager.Get("SteeringVelocityX");
string steeringVelY = TextManager.Get("SteeringVelocityY");
string steeringDepth = TextManager.Get("SteeringDepth");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), paddedStatusContainer.RectTransform), "")
{
TextGetter = () =>
{
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.Y * Physics.DisplayToRealWorldRatio) * 3.6f;
return steeringVelY.Replace("[kph]", ((int)-realWorldVel).ToString());
}
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), paddedStatusContainer.RectTransform), "")
{
TextGetter = () =>
{
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.X * Physics.DisplayToRealWorldRatio) * 3.6f;
return steeringVelX.Replace("[kph]", ((int)realWorldVel).ToString());
}
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), paddedStatusContainer.RectTransform), "")
{
TextGetter = () =>
{
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 steeringDepth.Replace("[m]", ((int)realWorldDepth).ToString());
}
};
pressureWarningText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), paddedStatusContainer.RectTransform), TextManager.Get("SteeringDepthWarning"), Color.Red)
{
Visible = false
};
tipContainer = new GUITextBlock(new RectTransform(new Vector2(0.25f, 0.12f), GuiFrame.RectTransform, Anchor.BottomLeft)
{ MinSize = new Point(150, 0), RelativeOffset = new Vector2(0.0f, -0.05f) }, "", wrap: true, style: "GUIToolTip")
{
AutoScale = true
};
noPowerTip = TextManager.Get("SteeringNoPowerTip");
autoPilotMaintainPosTip = TextManager.Get("SteeringAutoPilotMaintainPosTip");
autoPilotLevelStartTip = TextManager.Get("SteeringAutoPilotLocationTip").Replace("[locationname]",
GameMain.GameSession?.StartLocation == null ? "Start" : GameMain.GameSession.StartLocation.Name);
autoPilotLevelEndTip = TextManager.Get("SteeringAutoPilotLocationTip").Replace("[locationname]",
GameMain.GameSession?.EndLocation == null ? "End" : GameMain.GameSession.EndLocation.Name);
steerArea = new GUICustomComponent(new RectTransform(new Point(viewSize), GuiFrame.RectTransform, Anchor.CenterLeft),
(spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null);
}
private bool ToggleMaintainPosition(GUITickBox tickBox)
/// <summary>
/// Makes the sonar view CustomComponent render the steering HUD, preventing it from being drawn behing the sonar
/// </summary>
public void AttachToSonarHUD(GUICustomComponent sonarView)
{
unsentChanges = true;
levelStartTickBox.Selected = false;
levelEndTickBox.Selected = false;
if (item.Submarine == null)
{
posToMaintain = null;
}
else
{
posToMaintain = item.Submarine.WorldPosition;
}
tickBox.Selected = true;
return true;
steerArea.Visible = false;
sonarView.OnDraw += (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); };
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
public void DrawHUD(SpriteBatch spriteBatch, Rectangle rect)
{
//if (voltage < minVoltage) return;
int width = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
int x = GuiFrame.Rect.X;
int y = GuiFrame.Rect.Y;
GuiFrame.Draw(spriteBatch);
int width = rect.Width, height = rect.Height;
int x = rect.X;
int y = rect.Y;
if (voltage < minVoltage && currPowerConsumption > 0.0f) return;
Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40);
//GUI.DrawRectangle(spriteBatch, velRect, Color.White, false);
if (item.Submarine != null && Level.Loaded != null)
{
Vector2 realWorldVelocity = ConvertUnits.ToDisplayUnits(item.Submarine.Velocity * Physics.DisplayToRealWorldRatio) * 3.6f;
float realWorldDepth = Math.Abs(item.Submarine.Position.Y - Level.Loaded.Size.Y) * Physics.DisplayToRealWorldRatio;
GUI.DrawString(spriteBatch, new Vector2(x + 20, y + height - 65),
TextManager.Get("SteeringVelocityX").Replace("[kph]", ((int)realWorldVelocity.X).ToString()), Color.LightGreen, null, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(x + 20, y + height - 50),
TextManager.Get("SteeringVelocityY").Replace("[kph]", ((int)-realWorldVelocity.Y).ToString()), Color.LightGreen, null, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(x + 20, y + height - 30),
TextManager.Get("SteeringDepth").Replace("[m]", ((int)realWorldDepth).ToString()), Color.LightGreen, null, 0, GUI.SmallFont);
}
Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40);
GUI.DrawLine(spriteBatch,
new Vector2(velRect.Center.X, velRect.Center.Y),
new Vector2(velRect.Center.X + currVelocity.X, velRect.Center.Y - currVelocity.Y),
Color.Gray);
Vector2 targetVelPos = new Vector2(velRect.Center.X + targetVelocity.X, velRect.Center.Y - targetVelocity.Y);
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.X += velRect.Center.X;
steeringInputPos.Y += velRect.Center.Y;
GUI.DrawLine(spriteBatch,
new Vector2(velRect.Center.X, velRect.Center.Y),
steeringInputPos,
Color.LightGray);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringInputPos.X - 5, (int)steeringInputPos.Y - 5, 10, 10), Color.White);
//if (keyboardInput.Length() > 0 || Vector2.Distance(PlayerInput.MousePosition, new Vector2(velRect.Center.X, velRect.Center.Y)) < 200.0f)
if (velRect.Contains(PlayerInput.MousePosition))
{
GUI.DrawRectangle(spriteBatch, new Rectangle((int)steeringInputPos.X - 10, (int)steeringInputPos.Y - 10, 20, 20), Color.Red);
}
}
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 = velRect.Center.ToVector2() + displayPosToMaintain;
float crossHairSize = 8.0f;
Color crosshairColor = Color.Orange * (0.5f + ((float)Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) / 4.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);
Vector2 neutralPos = ((controlledSub.WorldPosition - sonar.DisplayOffset * sonar.Zoom - controlledSub.WorldPosition)) / sonar.Range * sonar.DisplayRadius * sonar.Zoom;
neutralPos.Y = -neutralPos.Y;
neutralPos = neutralPos.ClampLength(velRect.Width / 2);
neutralPos = velRect.Center.ToVector2() + neutralPos;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)neutralPos.X - 5, (int)neutralPos.Y - 5, 10, 10), Color.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.X += velRect.Center.X;
steeringPos.Y += velRect.Center.Y;
GUI.DrawLine(spriteBatch,
new Vector2(velRect.Center.X, velRect.Center.Y),
targetVelPos,
Color.LightGray);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)targetVelPos.X - 5, (int)targetVelPos.Y - 5, 10, 10), Color.White);
if (Vector2.Distance(PlayerInput.MousePosition, new Vector2(velRect.Center.X, velRect.Center.Y)) < 200.0f)
{
GUI.DrawRectangle(spriteBatch, new Rectangle((int)targetVelPos.X - 10, (int)targetVelPos.Y - 10, 20, 20), Color.Red);
}
steeringPos,
Color.CadetBlue, 0, 2);
}
public override void AddToGUIUpdateList()
public void DebugDrawHUD(SpriteBatch spriteBatch, Vector2 transducerCenter, float displayScale, float displayRadius, Vector2 center)
{
GuiFrame.AddToGUIUpdateList();
}
if (SteeringPath == null) return;
public override void UpdateHUD(Character character)
{
GuiFrame.Update(1.0f / 60.0f);
if (voltage < minVoltage && currPowerConsumption > 0.0f) return;
if (Vector2.Distance(PlayerInput.MousePosition, new Vector2(GuiFrame.Rect.Center.X, GuiFrame.Rect.Center.Y)) < 200.0f)
Vector2 prevPos = Vector2.Zero;
foreach (WayPoint wp in SteeringPath.Nodes)
{
if (PlayerInput.LeftButtonHeld())
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 : Color.Green, false);
if (prevPos != Vector2.Zero)
{
TargetVelocity = PlayerInput.MousePosition - new Vector2(GuiFrame.Rect.Center.X, GuiFrame.Rect.Center.Y);
targetVelocity.Y = -targetVelocity.Y;
GUI.DrawLine(spriteBatch, pos, prevPos, Color.Green);
}
unsentChanges = true;
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,
Color.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, Color.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(Color.Green, Color.Orange, obstacle.Dot), width: 2);
}
}
}
private bool SelectDestination(GUITickBox tickBox)
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
unsentChanges = true;
if (tickBox == levelStartTickBox)
if (steerArea.Rect.Contains(PlayerInput.MousePosition))
{
levelEndTickBox.Selected = false;
if (!PlayerInput.KeyDown(InputType.Select) && !PlayerInput.KeyHit(InputType.Select))
{
Character.DisableControls = true;
}
}
autoPilotControlsDisabler.Visible = !AutoPilot;
if (voltage < minVoltage && currPowerConsumption > 0.0f)
{
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.Distance(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerArea.Rect.Width / 2)
{
if (PlayerInput.LeftButtonHeld())
{
Vector2 inputPos = PlayerInput.MousePosition - steerArea.Rect.Center.ToVector2();
inputPos.Y = -inputPos.Y;
if (AutoPilot && !LevelStartSelected && !LevelEndSelected)
{
posToMaintain = controlledSub == null ?
item.WorldPosition :
controlledSub.WorldPosition + (sonar.DisplayOffset * sonar.Zoom) + inputPos / sonar.DisplayRadius * sonar.Range / sonar.Zoom;
}
else
{
SteeringInput = inputPos;
}
unsentChanges = true;
user = Character.Controlled;
}
}
if (!AutoPilot && Character.DisableControls)
{
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(Keys.LeftShift))
{
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
{
levelStartTickBox.Selected = false;
inputCumulation = 0;
keyboardInput = Vector2.Zero;
}
maintainPosTickBox.Selected = false;
posToMaintain = null;
tickBox.Selected = true;
UpdatePath();
float closestDist = DockingAssistThreshold * DockingAssistThreshold;
DockingModeEnabled = false;
DockingSource = null;
DockingTarget = null;
foreach (DockingPort sourcePort in DockingPort.List)
{
if (sourcePort.Docked || sourcePort.Item.Submarine == null) { continue; }
if (sourcePort.Item.Submarine != controlledSub) { continue; }
return true;
int sourceDir = sourcePort.IsHorizontal ?
Math.Sign(sourcePort.Item.WorldPosition.X - sourcePort.Item.Submarine.WorldPosition.X) :
Math.Sign(sourcePort.Item.WorldPosition.Y - sourcePort.Item.Submarine.WorldPosition.Y);
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.IsHorizontal ?
Math.Sign(targetPort.Item.WorldPosition.X - targetPort.Item.Submarine.WorldPosition.X) :
Math.Sign(targetPort.Item.WorldPosition.Y - targetPort.Item.Submarine.WorldPosition.Y);
if (sourceDir == targetDir) { continue; }
float dist = Vector2.DistanceSquared(sourcePort.Item.WorldPosition, targetPort.Item.WorldPosition);
if (dist < closestDist)
{
DockingModeEnabled = true;
DockingSource = sourcePort;
DockingTarget = targetPort;
}
}
}
}
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
@@ -180,8 +569,8 @@ namespace Barotrauma.Items.Components
if (!autoPilot)
{
//no need to write steering info if autopilot is controlling
msg.Write(targetVelocity.X);
msg.Write(targetVelocity.Y);
msg.Write(steeringInput.X);
msg.Write(steeringInput.Y);
}
else
{
@@ -202,11 +591,13 @@ namespace Barotrauma.Items.Components
{
long msgStartPos = msg.Position;
bool autoPilot = msg.ReadBoolean();
Vector2 newTargetVelocity = targetVelocity;
bool maintainPos = false;
Vector2? newPosToMaintain = null;
bool headingToStart = false;
bool autoPilot = msg.ReadBoolean();
Vector2 newSteeringInput = steeringInput;
Vector2 newTargetVelocity = targetVelocity;
float newSteeringAdjustSpeed = steeringAdjustSpeed;
bool maintainPos = false;
Vector2? newPosToMaintain = null;
bool headingToStart = false;
if (autoPilot)
{
@@ -224,7 +615,9 @@ namespace Barotrauma.Items.Components
}
else
{
newSteeringInput = new Vector2(msg.ReadFloat(), msg.ReadFloat());
newTargetVelocity = new Vector2(msg.ReadFloat(), msg.ReadFloat());
newSteeringAdjustSpeed = msg.ReadFloat();
}
if (correctionTimer > 0.0f)
@@ -239,11 +632,12 @@ namespace Barotrauma.Items.Components
if (!AutoPilot)
{
targetVelocity = newTargetVelocity;
SteeringInput = newSteeringInput;
TargetVelocity = newTargetVelocity;
steeringAdjustSpeed = newSteeringAdjustSpeed;
}
else
{
MaintainPos = newPosToMaintain != null;
posToMaintain = newPosToMaintain;
@@ -2,20 +2,48 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma.Items.Components
{
partial class PowerContainer : Powered, IDrawableComponent, IServerSerializable, IClientSerializable
{
private GUIProgressBar chargeIndicator;
private GUIScrollBar rechargeSpeedSlider;
partial void InitProjSpecific()
{
if (canBeSelected)
if (GuiFrame == null) return;
GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.7f), GuiFrame.RectTransform, Anchor.Center))
{ RelativeSpacing = 0.1f, Stretch = true };
string rechargeStr = TextManager.Get("PowerContainerRechargeRate");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform), "RechargeRate", textAlignment: Alignment.Center)
{
var button = new GUIButton(new Rectangle(160, 50, 30, 30), "-", "", GuiFrame);
button.OnClicked = (GUIButton btn, object obj) =>
TextGetter = () =>
{
RechargeSpeed = rechargeSpeed - maxRechargeSpeed * 0.1f;
return rechargeStr.Replace("[rate]", ((int)((rechargeSpeed / maxRechargeSpeed) * 100.0f)).ToString());
}
};
var sliderArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform, Anchor.BottomCenter), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), sliderArea.RectTransform),
"0 %", textAlignment: Alignment.Center);
rechargeSpeedSlider = new GUIScrollBar(new RectTransform(new Vector2(0.8f, 1.0f), sliderArea.RectTransform), barSize: 0.25f, style: "GUISlider")
{
Step = 0.1f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{
float newRechargeSpeed = maxRechargeSpeed * barScroll;
if (Math.Abs(newRechargeSpeed - rechargeSpeed) < 0.1f) return false;
RechargeSpeed = newRechargeSpeed;
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
@@ -26,68 +54,79 @@ namespace Barotrauma.Items.Components
item.CreateClientEvent(this);
correctionTimer = CorrectionDelay;
}
return true;
};
}
};
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), sliderArea.RectTransform),
"100 %", textAlignment: Alignment.Center);
button = new GUIButton(new Rectangle(200, 50, 30, 30), "+", "", GuiFrame);
button.OnClicked = (GUIButton btn, object obj) =>
string chargeStr = TextManager.Get("PowerContainerCharge");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform), "Charge", textAlignment: Alignment.Center)
{
TextGetter = () =>
{
RechargeSpeed = rechargeSpeed + maxRechargeSpeed * 0.1f;
return chargeStr.Replace("[charge]", (int)charge + "/" + (int)capacity).Replace("[percentage]", ((int)((charge / capacity) * 100.0f)).ToString());
}
};
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
GameServer.Log(Character.Controlled.LogName + " set the recharge speed of " + item.Name + " to " + (int)((rechargeSpeed / maxRechargeSpeed) * 100.0f) + " %", ServerLog.MessageType.ItemInteraction);
}
else if (GameMain.Client != null)
{
item.CreateClientEvent(this);
correctionTimer = CorrectionDelay;
}
chargeIndicator = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), barSize: 0.0f)
{
ProgressGetter = () =>
{
return charge / capacity;
}
};
}
return true;
};
public override void OnItemLoaded()
{
if (rechargeSpeedSlider != null)
{
rechargeSpeedSlider.BarScroll = rechargeSpeed / MaxRechargeSpeed;
}
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
float chargeRatio = charge / capacity;
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
}
public void Draw(SpriteBatch spriteBatch, bool editing = false)
{
if (indicatorSize.X <= 1.0f || indicatorSize.Y <= 1.0f) return;
GUI.DrawRectangle(spriteBatch,
new Vector2(item.DrawPosition.X - 4, -item.DrawPosition.Y),
new Vector2(8, 22), Color.Black);
new Vector2(
item.DrawPosition.X - item.Sprite.SourceRect.Width / 2 * item.Scale + indicatorPosition.X * item.Scale,
-item.DrawPosition.Y - item.Sprite.SourceRect.Height / 2 * item.Scale + indicatorPosition.Y * item.Scale),
indicatorSize * item.Scale, Color.Black, depth: item.SpriteDepth - 0.00001f);
if (charge > 0)
GUI.DrawRectangle(spriteBatch,
new Vector2(item.DrawPosition.X - 3, -item.DrawPosition.Y + 1 + (20.0f * (1.0f - charge / capacity))),
new Vector2(6, 20 * (charge / capacity)), Color.Green, true);
{
Color indicatorColor = ToolBox.GradientLerp(charge / capacity, Color.Red, Color.Orange, Color.Green);
if (!isHorizontal)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(
item.DrawPosition.X - item.Sprite.SourceRect.Width / 2 * item.Scale + indicatorPosition.X * item.Scale + 1,
-item.DrawPosition.Y - item.Sprite.SourceRect.Height / 2 * item.Scale + indicatorPosition.Y * item.Scale + 1 + ((indicatorSize.Y * item.Scale) * (1.0f - charge / capacity))),
new Vector2(indicatorSize.X * item.Scale - 2, (indicatorSize.Y * item.Scale - 2) * (charge / capacity)), indicatorColor, true,
depth: item.SpriteDepth - 0.00001f);
}
else
{
GUI.DrawRectangle(spriteBatch,
new Vector2(
item.DrawPosition.X - item.Sprite.SourceRect.Width / 2 * item.Scale + indicatorPosition.X * item.Scale + 1 ,
-item.DrawPosition.Y - item.Sprite.SourceRect.Height / 2 * item.Scale + indicatorPosition.Y * item.Scale + 1),
new Vector2((indicatorSize.X * item.Scale - 2) * (charge / capacity), indicatorSize.Y * item.Scale - 2), indicatorColor, true,
depth: item.SpriteDepth - 0.00001f);
}
}
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
GuiFrame.Draw(spriteBatch);
int x = GuiFrame.Rect.X;
int y = GuiFrame.Rect.Y;
//GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true);
GUI.Font.DrawString(spriteBatch,
TextManager.Get("PowerContainerCharge").Replace("[charge]", (int)charge + "/" + (int)capacity).Replace("[percentage]", ((int)((charge / capacity) * 100.0f)).ToString()),
new Vector2(x + 30, y + 30), Color.White);
GUI.Font.DrawString(spriteBatch, TextManager.Get("PowerContainerRechargeRate") + ": " + (int)((rechargeSpeed / maxRechargeSpeed) * 100.0f) + " %", new Vector2(x + 30, y + 95), Color.White);
}
public override void AddToGUIUpdateList()
{
GuiFrame.AddToGUIUpdateList();
}
public override void UpdateHUD(Character character)
{
GuiFrame.Update(1.0f / 60.0f);
}
public void ClientWrite(NetBuffer msg, object[] extraData)
{
msg.WriteRangedInteger(0, 10, (int)(rechargeSpeed / MaxRechargeSpeed * 10));
@@ -1,36 +1,73 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class PowerTransfer : Powered
{
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
private GUITickBox powerIndicator;
private GUITickBox highVoltageIndicator;
private GUITickBox lowVoltageIndicator;
partial void InitProjectSpecific(XElement element)
{
if (!canBeSelected) return;
if (GuiFrame == null) return;
int x = GuiFrame.Rect.X;
int y = GuiFrame.Rect.Y;
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), GuiFrame.RectTransform, Anchor.Center), style: null);
powerIndicator = new GUITickBox(new RectTransform(new Point(30, 30), paddedFrame.RectTransform),
TextManager.Get("PowerTransferPowered"), style: "IndicatorLightGreen")
{
Enabled = false
};
highVoltageIndicator = new GUITickBox(new RectTransform(new Point(30, 30), paddedFrame.RectTransform) { AbsoluteOffset = new Point(0, 40) },
TextManager.Get("PowerTransferHighVoltage"), style: "IndicatorLightRed")
{
ToolTip = TextManager.Get("PowerTransferTipOvervoltage"),
Enabled = false
};
lowVoltageIndicator = new GUITickBox(new RectTransform(new Point(30, 30), paddedFrame.RectTransform) { AbsoluteOffset = new Point(0, 80) },
TextManager.Get("PowerTransferLowVoltage"), style: "IndicatorLightRed")
{
ToolTip = TextManager.Get("PowerTransferTipLowvoltage"),
Enabled = false
};
GuiFrame.Draw(spriteBatch);
var textContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), paddedFrame.RectTransform, Anchor.TopRight));
GUI.Font.DrawString(spriteBatch,
TextManager.Get("PowerTransferPower").Replace("[power]", ((int)(-currPowerConsumption)).ToString()),
new Vector2(x + 30, y + 30), Color.White);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContainer.RectTransform),
TextManager.Get("PowerTransferPowerLabel"), font: GUI.LargeFont)
{
ToolTip = TextManager.Get("PowerTransferTipPower")
};
string powerStr = TextManager.Get("PowerTransferPower");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), textContainer.RectTransform), "", textColor: Color.LightGreen)
{
ToolTip = TextManager.Get("PowerTransferTipPower"),
TextGetter = () => { return powerStr.Replace("[power]", ((int)(-currPowerConsumption)).ToString()); }
};
GUI.Font.DrawString(spriteBatch,
TextManager.Get("PowerTransferLoad").Replace("[load]", ((int)powerLoad).ToString()),
new Vector2(x + 30, y + 100), Color.White);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContainer.RectTransform),
TextManager.Get("PowerTransferLoadLabel"), font: GUI.LargeFont)
{
ToolTip = TextManager.Get("PowerTransferTipLoad")
};
string loadStr = TextManager.Get("PowerTransferLoad");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), textContainer.RectTransform), "", textColor: Color.LightBlue)
{
ToolTip = TextManager.Get("PowerTransferTipLoad"),
TextGetter = () => { return loadStr.Replace("[load]", ((int)(powerLoad)).ToString()); }
};
}
public override void AddToGUIUpdateList()
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
GuiFrame.AddToGUIUpdateList();
}
if (GuiFrame == null) return;
public override void UpdateHUD(Character character)
{
GuiFrame.Update(1.0f / 60.0f);
float voltage = powerLoad <= 0.0f ? 1.0f : -currPowerConsumption / powerLoad;
powerIndicator.Selected = IsActive && currPowerConsumption < -0.1f;
highVoltageIndicator.Selected = Timing.TotalTime % 0.5f < 0.25f && powerIndicator.Selected && voltage > 1.2f;
lowVoltageIndicator.Selected = Timing.TotalTime % 0.5f < 0.25f && powerIndicator.Selected && voltage < 0.8f;
}
}
}
@@ -1,11 +1,31 @@
namespace Barotrauma.Items.Components
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.Sounds;
namespace Barotrauma.Items.Components
{
partial class Powered : ItemComponent
{
protected static Sound[] sparkSounds;
protected List<RoundSound> sparkSounds;
private RoundSound powerOnSound;
private bool powerOnSoundPlayed;
private static Sound powerOnSound;
partial void InitProjectSpecific(XElement element)
{
sparkSounds = new List<RoundSound>();
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "poweronsound":
powerOnSound = Submarine.LoadRoundSound(subElement, false);
break;
case "sparksound":
sparkSounds.Add(Submarine.LoadRoundSound(subElement, false));
break;
}
}
}
}
}
@@ -0,0 +1,126 @@
using Barotrauma.Particles;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class RepairTool
{
public ParticleEmitter ParticleEmitter
{
get;
private set;
}
private List<ParticleEmitter> ParticleEmitterHitStructure = new List<ParticleEmitter>();
private List<ParticleEmitter> ParticleEmitterHitCharacter = new List<ParticleEmitter>();
private List<Pair<RelatedItem, ParticleEmitter>> ParticleEmitterHitItem = new List<Pair<RelatedItem, ParticleEmitter>>();
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "particleemitter":
ParticleEmitter = new ParticleEmitter(subElement);
break;
case "particleemitterhititem":
string[] identifiers = subElement.GetAttributeStringArray("identifiers", new string[0]);
if (identifiers.Length == 0) identifiers = subElement.GetAttributeStringArray("identifier", new string[0]);
string[] excludedIdentifiers = subElement.GetAttributeStringArray("excludedidentifiers", new string[0]);
if (excludedIdentifiers.Length == 0) excludedIdentifiers = subElement.GetAttributeStringArray("excludedidentifier", new string[0]);
ParticleEmitterHitItem.Add(
new Pair<RelatedItem, ParticleEmitter>(
new RelatedItem(identifiers, excludedIdentifiers),
new ParticleEmitter(subElement)));
break;
case "particleemitterhitstructure":
ParticleEmitterHitStructure.Add(new ParticleEmitter(subElement));
break;
case "particleemitterhitcharacter":
ParticleEmitterHitCharacter.Add(new ParticleEmitter(subElement));
break;
}
}
}
partial void UseProjSpecific(float deltaTime)
{
if (ParticleEmitter != null)
{
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
ParticleEmitter.Emit(
deltaTime, item.WorldPosition + TransformedBarrelPos,
item.CurrentHull, particleAngle, -particleAngle);
}
}
partial void FixStructureProjSpecific(Character user, float deltaTime, Structure targetStructure, int sectionIndex)
{
Vector2 progressBarPos = targetStructure.SectionPosition(sectionIndex);
if (targetStructure.Submarine != null)
{
progressBarPos += targetStructure.Submarine.DrawPosition;
}
var progressBar = user.UpdateHUDProgressBar(
targetStructure,
progressBarPos,
1.0f - targetStructure.SectionDamage(sectionIndex) / targetStructure.Health,
Color.Red, Color.Green);
if (progressBar != null) progressBar.Size = new Vector2(60.0f, 20.0f);
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
if (targetStructure.Submarine != null) particlePos += targetStructure.Submarine.DrawPosition;
foreach (var emitter in ParticleEmitterHitStructure)
{
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
}
}
partial void FixCharacterProjSpecific(Character user, float deltaTime, Character targetCharacter)
{
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
if (targetCharacter.Submarine != null) particlePos += targetCharacter.Submarine.DrawPosition;
foreach (var emitter in ParticleEmitterHitCharacter)
{
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
}
}
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem, float prevCondition)
{
if (prevCondition != targetItem.Condition)
{
Vector2 progressBarPos = targetItem.DrawPosition;
var progressBar = user.UpdateHUDProgressBar(
targetItem,
progressBarPos,
targetItem.Condition / 100.0f,
Color.Red, Color.Green);
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
}
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
if (targetItem.Submarine != null) particlePos += targetItem.Submarine.DrawPosition;
foreach (var emitter in ParticleEmitterHitItem)
{
if (!emitter.First.MatchesItem(targetItem)) continue;
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
}
}
}
}
@@ -0,0 +1,130 @@
using Barotrauma.Particles;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Repairable : ItemComponent
{
private GUIButton repairButton;
private GUIProgressBar progressBar;
private List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
//the corresponding particle emitter is active when the condition is within this range
private List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
private string repairButtonText, repairingText;
[Serialize("", false)]
public string Description
{
get;
set;
}
public override bool ShouldDrawHUD(Character character)
{
if (!HasRequiredItems(character, false)) return false;
return (item.Condition < ShowRepairUIThreshold || (currentFixer == character && item.Condition < item.Prefab.Health));
}
partial void InitProjSpecific(XElement element)
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
Stretch = true,
RelativeSpacing = 0.05f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform),
header, textAlignment: Alignment.TopCenter, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
Description, font: GUI.SmallFont, wrap: true);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform),
TextManager.Get("RequiredRepairSkills"));
for (int i = 0; i < requiredSkills.Count; i++)
{
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform),
" - " + TextManager.Get("SkillName." + requiredSkills[i].Identifier) + ": " + ((int)requiredSkills[i].Level), font: GUI.SmallFont)
{
UserData = requiredSkills[i]
};
}
progressBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform),
color: Color.Green, barSize: 0.0f);
repairButtonText = TextManager.Get("RepairButton");
repairingText = TextManager.Get("Repairing");
repairButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.TopCenter),
repairButtonText)
{
OnClicked = (btn, obj) =>
{
currentFixer = Character.Controlled;
item.CreateClientEvent(this);
return true;
}
};
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "emitter":
case "particleemitter":
particleEmitters.Add(new ParticleEmitter(subElement));
particleEmitterConditionRanges.Add(new Vector2(
subElement.GetAttributeFloat("mincondition", 0.0f),
subElement.GetAttributeFloat("maxcondition", 100.0f)));
break;
}
}
}
partial void UpdateProjSpecific(float deltaTime)
{
for (int i = 0; i < particleEmitters.Count; i++)
{
if (item.Condition >= particleEmitterConditionRanges[i].X && item.Condition <= particleEmitterConditionRanges[i].Y)
{
particleEmitters[i].Emit(deltaTime, item.WorldPosition, item.CurrentHull);
}
}
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
IsActive = true;
progressBar.BarSize = item.Condition / item.Prefab.Health;
progressBar.Color = ToolBox.GradientLerp(item.Condition / item.Prefab.Health, Color.Red, Color.Orange, Color.Green);
repairButton.Enabled = currentFixer == null;
repairButton.Text = currentFixer == null ?
repairButtonText :
repairingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
{
Skill skill = c.UserData as Skill;
if (skill == null) continue;
GUITextBlock textBlock = (GUITextBlock)c;
if (character.GetSkillLevel(skill.Identifier) < skill.Level)
{
textBlock.TextColor = Color.Red;
}
else
{
textBlock.TextColor = Color.White;
}
}
}
}
}
@@ -2,27 +2,27 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Items.Components
{
partial class Connection
{
private static Texture2D panelTexture;
//private static Texture2D panelTexture;
private static Sprite connector;
private static Sprite wireVertical;
private static Sprite connectionSprite;
private static Sprite connectionSpriteHighlight;
private static List<Sprite> screwSprites;
private static Wire draggingConnected;
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Character character)
{
int width = 400, height = 200;
int x = GameMain.GraphicsWidth / 2 - width / 2, y = GameMain.GraphicsHeight - height;
Rectangle panelRect = new Rectangle(x, y, width, height);
spriteBatch.Draw(panelTexture, panelRect, new Rectangle(0, 512 - height, width, height), Color.White);
//GUI.DrawRectangle(spriteBatch, panelRect, Color.Black, true);
Rectangle panelRect = panel.GuiFrame.Rect;
int x = panelRect.X, y = panelRect.Y;
int width = panelRect.Width, height = panelRect.Height;
bool mouseInRect = panelRect.Contains(PlayerInput.MousePosition);
@@ -49,14 +49,15 @@ namespace Barotrauma.Items.Components
}
}
Vector2 rightPos = new Vector2(x + width - 130, y + 50);
Vector2 leftPos = new Vector2(x + 130, y + 50);
Vector2 rightPos = new Vector2(x + width - 130, y + 80);
Vector2 leftPos = new Vector2(x + 130, y + 80);
Vector2 rightWirePos = new Vector2(x + width - 5, y + 30);
Vector2 leftWirePos = new Vector2(x + 5, y + 30);
int wireInterval = (height - 20) / Math.Max(totalWireCount, 1);
int connectorIntervalLeft = (height - 100) / Math.Max(panel.Connections.Count(c => c.IsOutput), 1);
int connectorIntervalRight = (height - 100) / Math.Max(panel.Connections.Count(c => !c.IsOutput), 1);
foreach (Connection c in panel.Connections)
{
@@ -67,7 +68,7 @@ namespace Barotrauma.Items.Components
int linkIndex = c.FindWireIndex(draggingConnected.Item);
if (linkIndex > -1)
{
Inventory.draggingItem = c.Wires[linkIndex].Item;
Inventory.draggingItem = c.wires[linkIndex].Item;
}
}
@@ -80,7 +81,7 @@ namespace Barotrauma.Items.Components
mouseInRect, equippedWire,
wireInterval);
rightPos.Y += 30;
rightPos.Y += connectorIntervalLeft;
rightWirePos.Y += c.Wires.Count(w => w != null) * wireInterval;
}
else
@@ -91,7 +92,7 @@ namespace Barotrauma.Items.Components
mouseInRect, equippedWire,
wireInterval);
leftPos.Y += 30;
leftPos.Y += connectorIntervalRight;
leftWirePos.Y += c.Wires.Count(w => w != null) * wireInterval;
//leftWireX -= wireInterval;
}
@@ -99,7 +100,7 @@ namespace Barotrauma.Items.Components
if (draggingConnected != null)
{
DrawWire(spriteBatch, draggingConnected, draggingConnected.Item, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height), mouseInRect, null, panel);
DrawWire(spriteBatch, draggingConnected, draggingConnected.Item, PlayerInput.MousePosition, new Vector2(x + width / 2, y + height - 10), mouseInRect, null, panel, "");
if (!PlayerInput.LeftButtonHeld())
{
@@ -124,48 +125,45 @@ namespace Barotrauma.Items.Components
{
DrawWire(spriteBatch, equippedWire, equippedWire.Item,
new Vector2(x + width / 2, y + height - 100),
new Vector2(x + width / 2, y + height), mouseInRect, null, panel);
new Vector2(x + width / 2, y + height), mouseInRect, null, panel, "");
if (draggingConnected == equippedWire) Inventory.draggingItem = equippedWire.Item;
}
}
//stop dragging a wire item if cursor is outside the panel
if (mouseInRect) Inventory.draggingItem = null;
spriteBatch.Draw(panelTexture, panelRect, new Rectangle(0, 0, width, height), Color.White);
//stop dragging a wire item if the cursor is within any connection panel
//(so we don't drop the item when dropping the wire on a connection)
if (mouseInRect || GUI.MouseOn?.UserData is ConnectionPanel) Inventory.draggingItem = null;
}
private void Draw(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 labelPos, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval)
{
//spriteBatch.DrawString(GUI.SmallFont, Name, new Vector2(labelPos.X, labelPos.Y-10), Color.White);
GUI.DrawString(spriteBatch, labelPos, Name, IsPower ? Color.Red : Color.White, Color.Black, 0, GUI.SmallFont);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)position.X - 10, (int)position.Y - 10, 20, 20), Color.White);
spriteBatch.Draw(panelTexture, position - new Vector2(16.0f, 16.0f), new Rectangle(64, 256, 32, 32), Color.White);
connectionSprite.Draw(spriteBatch, position);
for (int i = 0; i < MaxLinked; i++)
{
if (Wires[i] == null || Wires[i].Hidden || draggingConnected == Wires[i]) continue;
if (wires[i] == null || wires[i].Hidden || draggingConnected == wires[i]) continue;
Connection recipient = Wires[i].OtherConnection(this);
DrawWire(spriteBatch, Wires[i], (recipient == null) ? Wires[i].Item : recipient.item, position, wirePosition, mouseIn, equippedWire, panel);
Connection recipient = wires[i].OtherConnection(this);
string label = recipient == null ? "" :
wires[i].Locked ? recipient.item.Name + "\n" + TextManager.Get("ConnectionLocked") : recipient.item.Name;
DrawWire(spriteBatch, wires[i], (recipient == null) ? wires[i].Item : recipient.item, position, wirePosition, mouseIn, equippedWire, panel, label);
wirePosition.Y += wireInterval;
}
if (draggingConnected != null && Vector2.Distance(position, PlayerInput.MousePosition) < 13.0f)
{
spriteBatch.Draw(panelTexture, position - new Vector2(21.5f, 21.5f), new Rectangle(106, 250, 43, 43), Color.White);
connectionSpriteHighlight.Draw(spriteBatch, position);
if (!PlayerInput.LeftButtonHeld())
{
//find an empty cell for the new connection
int index = FindWireIndex(null);
int index = FindEmptyIndex();
if (index > -1 && !Wires.Contains(draggingConnected))
{
bool alreadyConnected = draggingConnected.IsConnectedTo(panel.Item);
@@ -186,22 +184,20 @@ namespace Barotrauma.Items.Components
Item.Name + " (" + Name + ") to " + otherConnection.item.Name + " (" + otherConnection.Name + ")", ServerLog.MessageType.ItemInteraction);
}
AddLink(index, draggingConnected);
SetWire(index, draggingConnected);
}
}
}
}
int screwIndex = (position.Y % 60 < 30) ? 0 : 1;
if (Wires.Any(w => w != null && w != draggingConnected))
{
spriteBatch.Draw(panelTexture, position - new Vector2(16.0f, 16.0f), new Rectangle(screwIndex * 32, 256, 32, 32), Color.White);
int screwIndex = (int)Math.Floor(position.Y / 30.0f) % screwSprites.Count;
screwSprites[screwIndex].Draw(spriteBatch, position);
}
}
private static void DrawWire(SpriteBatch spriteBatch, Wire wire, Item item, Vector2 end, Vector2 start, bool mouseIn, Wire equippedWire, ConnectionPanel panel)
private static void DrawWire(SpriteBatch spriteBatch, Wire wire, Item item, Vector2 end, Vector2 start, bool mouseIn, Wire equippedWire, ConnectionPanel panel, string label)
{
if (draggingConnected == wire)
{
@@ -228,13 +224,14 @@ namespace Barotrauma.Items.Components
Vector2.Distance(end, PlayerInput.MousePosition) < 20.0f ||
new Rectangle((start.X < end.X) ? textX - 100 : textX, (int)start.Y - 5, 100, 14).Contains(PlayerInput.MousePosition));
string label = wire.Locked || panel.Locked ? item.Name + "\n" + TextManager.Get("ConnectionLocked") : item.Name;
GUI.DrawString(spriteBatch,
new Vector2(start.X < end.X ? textX - GUI.SmallFont.MeasureString(label).X : textX, start.Y - 5.0f),
label,
(mouseOn ? Color.Gold : Color.White) * (wire.Locked ? 0.6f : 1.0f), Color.Black * 0.8f,
3, GUI.SmallFont);
if (!string.IsNullOrEmpty(label))
{
GUI.DrawString(spriteBatch,
new Vector2(start.X < end.X ? textX - GUI.SmallFont.MeasureString(label).X : textX, start.Y - 5.0f),
label,
(mouseOn ? Color.Gold : Color.White) * (wire.Locked ? 0.6f : 1.0f), Color.Black * 0.8f,
3, GUI.SmallFont);
}
var wireEnd = end + Vector2.Normalize(start - end) * 30.0f;
@@ -1,15 +1,44 @@
using Barotrauma.Networking;
using Lidgren.Network;
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 ConnectionPanel : ItemComponent, IServerSerializable, IClientSerializable
{
public override void UpdateHUD(Character character)
public static Wire HighlightedWire;
partial void InitProjSpecific(XElement element)
{
if (GuiFrame == null) return;
new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform), DrawConnections, null)
{
UserData = this
};
}
public override void Move(Vector2 amount)
{
if (item.Submarine == null || item.Submarine.Loading || Screen.Selected != GameMain.SubEditorScreen) return;
MoveConnectedWires(amount);
}
public override bool ShouldDrawHUD(Character character)
{
return character == Character.Controlled && character == user;
}
public override void AddToGUIUpdateList()
{
GuiFrame?.AddToGUIUpdateList();
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (character != Character.Controlled || character != user) return;
@@ -21,12 +50,17 @@ namespace Barotrauma.Items.Components
}
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
private void DrawConnections(SpriteBatch spriteBatch, GUICustomComponent container)
{
if (character != Character.Controlled || character != user) return;
if (user != Character.Controlled || user == null) return;
HighlightedWire = null;
Connection.DrawConnections(spriteBatch, this, character);
Connection.DrawConnections(spriteBatch, this, user);
foreach (UISprite sprite in GUI.Style.GetComponentStyle("ConnectionPanelFront").Sprites[GUIComponent.ComponentState.None])
{
sprite.Draw(spriteBatch, GuiFrame.Rect, Color.White, SpriteEffects.None);
}
}
@@ -47,7 +81,7 @@ namespace Barotrauma.Items.Components
private void ApplyRemoteState(NetBuffer msg)
{
List<Wire> prevWires = Connections.SelectMany(c => Array.FindAll(c.Wires, w => w != null)).ToList();
List<Wire> prevWires = Connections.SelectMany(c => c.Wires.Where(w => w != null)).ToList();
List<Wire> newWires = new List<Wire>();
foreach (Connection connection in Connections)
@@ -69,7 +103,7 @@ namespace Barotrauma.Items.Components
newWires.Add(wireComponent);
connection.Wires[i] = wireComponent;
connection.SetWire(i, wireComponent);
wireComponent.Connect(connection, false);
}
}
@@ -0,0 +1,114 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class CustomInterface
{
private List<GUIComponent> uiElements = new List<GUIComponent>();
partial void InitProjSpecific(XElement element)
{
uiElements.Clear();
GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), GuiFrame.RectTransform, Anchor.Center))
{ RelativeSpacing = 0.05f, Stretch = true };
foreach (CustomInterfaceElement ciElement in customInterfaceElementList)
{
if (ciElement.ContinuousSignal)
{
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), ciElement.Label)
{
UserData = ciElement
};
tickBox.OnSelected += (tBox) =>
{
if (GameMain.Client == null)
{
TickBoxToggled(tBox.UserData as CustomInterfaceElement, tBox.Selected);
}
else
{
item.CreateClientEvent(this);
}
return true;
};
uiElements.Add(tickBox);
}
else
{
var btn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), ciElement.Label)
{
UserData = ciElement
};
btn.OnClicked += (_, userdata) =>
{
if (GameMain.Client == null)
{
ButtonClicked(userdata as CustomInterfaceElement);
}
else
{
GameMain.Client.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.components.IndexOf(this), userdata as CustomInterfaceElement });
}
return true;
};
uiElements.Add(btn);
}
}
}
partial void UpdateLabelsProjSpecific()
{
for (int i = 0; i < labels.Length && i < uiElements.Count; i++)
{
if (uiElements[i] is GUIButton button)
{
button.Text = labels[i];
}
else if (uiElements[i] is GUITickBox tickBox)
{
tickBox.Text = labels[i];
}
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//extradata contains an array of buttons clicked by the player (or nothing if the player didn't click anything)
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
if (customInterfaceElementList[i].ContinuousSignal)
{
msg.Write(((GUITickBox)uiElements[i]).Selected);
}
else
{
msg.Write(extraData != null && extraData.Any(d => d as CustomInterfaceElement == customInterfaceElementList[i]));
}
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
bool elementState = msg.ReadBoolean();
if (customInterfaceElementList[i].ContinuousSignal)
{
((GUITickBox)uiElements[i]).Selected = elementState;
TickBoxToggled(customInterfaceElementList[i], elementState);
}
else if (elementState)
{
ButtonClicked(customInterfaceElementList[i]);
}
}
}
}
}
@@ -0,0 +1,18 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
partial class MotionSensor : IDrawableComponent
{
public void Draw(SpriteBatch spriteBatch, bool editing)
{
if (!editing || !MapEntity.SelectedList.Contains(item)) return;
Vector2 pos = item.WorldPosition + detectOffset;
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, pos - new Vector2(rangeX, rangeY), new Vector2(rangeX, rangeY) * 2.0f, Color.Cyan * 0.5f, isFilled: false, thickness: 2);
}
}
}
@@ -0,0 +1,18 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
partial class WifiComponent : IDrawableComponent
{
public void Draw(SpriteBatch spriteBatch, bool editing)
{
if (!editing || !MapEntity.SelectedList.Contains(item)) return;
Vector2 pos = new Vector2(item.DrawPosition.X, -item.DrawPosition.Y);
ShapeExtensions.DrawLine(spriteBatch, pos + Vector2.UnitY * range, pos - Vector2.UnitY * range, Color.Cyan * 0.5f, 2);
ShapeExtensions.DrawLine(spriteBatch, pos + Vector2.UnitX * range, pos - Vector2.UnitX * range, Color.Cyan * 0.5f, 2);
ShapeExtensions.DrawCircle(spriteBatch, pos, range, 32, Color.Cyan * 0.5f, 3);
}
}
}
@@ -35,8 +35,7 @@ namespace Barotrauma.Items.Components
SpriteEffects.None,
depth);
}
}
}
private static Sprite wireSprite;
private static Wire draggingWire;
@@ -45,7 +44,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing)
{
if (sections.Count == 0 && !IsActive)
if (sections.Count == 0 && !IsActive || Hidden)
{
Drawable = false;
return;
@@ -187,8 +186,7 @@ namespace Barotrauma.Items.Components
else
{
//check if close enough to a node
float temp = 0.0f;
int closestIndex = selectedWire.GetClosestNodeIndex(mousePos, nodeSelectDist, out temp);
int closestIndex = selectedWire.GetClosestNodeIndex(mousePos, nodeSelectDist, out _);
if (closestIndex > -1)
{
highlightedNodeIndex = closestIndex;
@@ -209,37 +207,40 @@ namespace Barotrauma.Items.Components
}
}
//check which wire is highlighted with the cursor
Wire highlighted = null;
float closestDist = float.PositiveInfinity;
foreach (Wire w in wires)
//check which wire is highlighted with the cursor
if (GUI.MouseOn == null)
{
Vector2 mousePos = GameMain.SubEditorScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
if (w.item.Submarine != null) mousePos -= (w.item.Submarine.Position + w.item.Submarine.HiddenSubPosition);
float dist = 0.0f;
int highlightedNode = w.GetClosestNodeIndex(mousePos, highlighted == null ? nodeSelectDist : closestDist, out dist);
if (highlightedNode > -1)
float closestDist = float.PositiveInfinity;
foreach (Wire w in wires)
{
if (dist < closestDist)
Vector2 mousePos = GameMain.SubEditorScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
if (w.item.Submarine != null) mousePos -= (w.item.Submarine.Position + w.item.Submarine.HiddenSubPosition);
int highlightedNode = w.GetClosestNodeIndex(mousePos, highlighted == null ? nodeSelectDist : closestDist, out float dist);
if (highlightedNode > -1)
{
highlightedNodeIndex = highlightedNode;
highlighted = w;
closestDist = dist;
if (dist < closestDist)
{
highlightedNodeIndex = highlightedNode;
highlighted = w;
closestDist = dist;
}
}
if (w.GetClosestSectionIndex(mousePos, highlighted == null ? sectionSelectDist : closestDist, out dist) > -1)
{
//prefer nodes over sections
if (dist + nodeSelectDist * 0.5f < closestDist)
{
highlightedNodeIndex = null;
highlighted = w;
closestDist = dist + nodeSelectDist * 0.5f;
}
}
}
if (w.GetClosestSectionIndex(mousePos, highlighted == null ? sectionSelectDist : closestDist, out dist) > -1)
{
//prefer nodes over sections
if (dist + nodeSelectDist * 0.5f < closestDist)
{
highlightedNodeIndex = null;
highlighted = w;
closestDist = dist + nodeSelectDist * 0.5f;
}
}
}
}
if (highlighted != null)
{
@@ -137,53 +137,77 @@ namespace Barotrauma.Items.Components
hudPos += Vector2.UnitX * 50.0f;
List<string> texts = new List<string>();
List<Color> textColors = new List<Color>();
if (target.Info != null)
{
texts.Add(target.Name);
textColors.Add(Color.White);
}
if (target.IsDead)
{
texts.Add(TextManager.Get("Deceased"));
texts.Add(TextManager.Get("CauseOfDeath") + ": " + TextManager.Get("CauseOfDeath." + target.CauseOfDeath.ToString()));
textColors.Add(Color.Red);
texts.Add(
target.CauseOfDeath.Affliction?.CauseOfDeathDescription ??
TextManager.Get("CauseOfDeath") + ": " +TextManager.Get("CauseOfDeath." + target.CauseOfDeath.Type.ToString()));
textColors.Add(Color.Red);
}
else
{
if (target.IsUnconscious) texts.Add(TextManager.Get("Unconscious"));
if (target.Stun > 0.01f) texts.Add(TextManager.Get("Stunned"));
int healthTextIndex = target.Health > 95.0f ? 0 :
MathHelper.Clamp((int)Math.Ceiling((1.0f - (target.Health / 200.0f + 0.5f)) * HealthTexts.Length), 0, HealthTexts.Length - 1);
texts.Add(HealthTexts[healthTextIndex]);
if (target.IsUnconscious)
{
texts.Add(TextManager.Get("Unconscious"));
textColors.Add(Color.Orange);
}
if (target.Stun > 0.01f)
{
texts.Add(TextManager.Get("Stunned"));
textColors.Add(Color.Orange);
}
int oxygenTextIndex = MathHelper.Clamp((int)Math.Floor((1.0f - (target.Oxygen / 100.0f)) * OxygenTexts.Length), 0, OxygenTexts.Length - 1);
texts.Add(OxygenTexts[oxygenTextIndex]);
textColors.Add(Color.Lerp(Color.Red, Color.Green, target.Oxygen / 100.0f));
if (target.Bleeding > 0.0f)
{
int bleedingTextIndex = MathHelper.Clamp((int)Math.Floor(target.Bleeding / 4.0f) * BleedingTexts.Length, 0, BleedingTexts.Length - 1);
int bleedingTextIndex = MathHelper.Clamp((int)Math.Floor(target.Bleeding / 100.0f) * BleedingTexts.Length, 0, BleedingTexts.Length - 1);
texts.Add(BleedingTexts[bleedingTextIndex]);
textColors.Add(Color.Lerp(Color.Red, Color.Green, target.Bleeding / 100.0f));
}
if (target.huskInfection != null)
var allAfflictions = target.CharacterHealth.GetAllAfflictions();
Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>();
foreach (Affliction affliction in allAfflictions)
{
if (target.huskInfection.State == HuskInfection.InfectionState.Transition)
if (affliction.Strength < affliction.Prefab.ActivationThreshold || affliction.Strength < affliction.Prefab.ShowIconThreshold) continue;
if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab))
{
texts.Add(TextManager.Get("HuskInfectionTransition"));
combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength;
}
else if (target.huskInfection.State == HuskInfection.InfectionState.Active)
else
{
texts.Add(TextManager.Get("HuskInfectionActive"));
combinedAfflictionStrengths[affliction.Prefab] = affliction.Strength;
}
}
foreach (AfflictionPrefab affliction in combinedAfflictionStrengths.Keys)
{
texts.Add(affliction.Name + ": " + ((int)combinedAfflictionStrengths[affliction]).ToString() + " %");
textColors.Add(Color.Lerp(Color.Red, Color.Green, combinedAfflictionStrengths[affliction] / affliction.MaxStrength));
}
}
foreach (string text in texts)
GUI.DrawString(spriteBatch, hudPos, texts[0], textColors[0] * alpha, Color.Black * 0.7f * alpha, 2);
hudPos.X += 5.0f;
hudPos.Y += 24.0f;
for (int i = 1; i < texts.Count; i++)
{
GUI.DrawString(spriteBatch, hudPos, text, Color.LightGreen * alpha, Color.Black * 0.7f * alpha, 2);
hudPos.Y += 24.0f;
GUI.DrawString(spriteBatch, hudPos, texts[i], textColors[i] * alpha, Color.Black * 0.7f * alpha, 2, GUI.SmallFont);
hudPos.Y += 18.0f;
}
}
}
@@ -1,4 +1,6 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -12,10 +14,25 @@ namespace Barotrauma.Items.Components
{
partial class Turret : Powered, IDrawableComponent, IServerSerializable
{
private Sprite crosshairSprite, disabledCrossHairSprite;
private Sprite crosshairSprite, crosshairPointerSprite;
private GUIProgressBar powerIndicator;
private float recoilTimer;
private RoundSound startMoveSound, endMoveSound, moveSound;
private SoundChannel moveSoundChannel;
private Vector2 crosshairPos, crosshairPointerPos;
private bool flashLowPower;
private bool flashNoAmmo;
private float flashTimer;
private float flashLength = 1;
private List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
[Editable, Serialize("0.0,0.0,0.0,0.0", true)]
public Color HudTint
{
@@ -37,6 +54,13 @@ namespace Barotrauma.Items.Components
private set;
}
[Serialize(0.0f, false)]
public float RecoilDistance
{
get;
private set;
}
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
@@ -47,77 +71,220 @@ namespace Barotrauma.Items.Components
case "crosshair":
crosshairSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.ConfigFile));
break;
case "disabledcrosshair":
disabledCrossHairSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.ConfigFile));
case "crosshairpointer":
crosshairPointerSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.ConfigFile));
break;
case "startmovesound":
startMoveSound = Submarine.LoadRoundSound(subElement, false);
break;
case "endmovesound":
endMoveSound = Submarine.LoadRoundSound(subElement, false);
break;
case "movesound":
moveSound = Submarine.LoadRoundSound(subElement, false);
break;
case "particleemitter":
particleEmitters.Add(new ParticleEmitter(subElement));
break;
}
}
int barWidth = 200;
powerIndicator = new GUIProgressBar(new Rectangle(GameMain.GraphicsWidth / 2 - barWidth / 2, 20, barWidth, 30), Color.White, 0.0f);
powerIndicator = new GUIProgressBar(new RectTransform(new Vector2(0.18f, 0.03f), GUI.Canvas, Anchor.TopCenter)
{
MinSize = new Point(100,20),
RelativeOffset = new Vector2(0.0f, 0.01f)
},
barSize: 0.0f);
}
partial void LaunchProjSpecific()
{
recoilTimer = Math.Max(Reload, 0.1f);
PlaySound(ActionType.OnUse, item.WorldPosition);
Vector2 particlePos = new Vector2(item.WorldRect.X + transformedBarrelPos.X, item.WorldRect.Y - transformedBarrelPos.Y);
foreach (ParticleEmitter emitter in particleEmitters)
{
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: rotation, particleRotation: rotation);
}
}
partial void UpdateProjSpecific(float deltaTime)
{
recoilTimer -= deltaTime;
if (crosshairSprite != null)
{
Vector2 itemPos = cam.WorldToScreen(new Vector2(item.WorldRect.X + transformedBarrelPos.X, item.WorldRect.Y - transformedBarrelPos.Y));
Vector2 turretDir = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
Vector2 mouseDiff = itemPos - PlayerInput.MousePosition;
crosshairPos = new Vector2(
MathHelper.Clamp(itemPos.X + turretDir.X * mouseDiff.Length(), 0, GameMain.GraphicsWidth),
MathHelper.Clamp(itemPos.Y + turretDir.Y * mouseDiff.Length(), 0, GameMain.GraphicsHeight));
}
crosshairPointerPos = PlayerInput.MousePosition;
if (Math.Abs(angularVelocity) > 0.1f)
{
if (moveSoundChannel == null && startMoveSound != null)
{
moveSoundChannel = SoundPlayer.PlaySound(startMoveSound.Sound, startMoveSound.Volume, startMoveSound.Range, item.WorldPosition);
}
else if (moveSoundChannel == null || !moveSoundChannel.IsPlaying)
{
if (moveSound != null)
{
moveSoundChannel.Dispose();
moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, moveSound.Volume, moveSound.Range, item.WorldPosition);
if (moveSoundChannel != null) moveSoundChannel.Looping = true;
}
}
}
else if (Math.Abs(angularVelocity) < 0.05f)
{
if (moveSoundChannel != null)
{
if (endMoveSound != null && moveSoundChannel.Sound != endMoveSound.Sound)
{
moveSoundChannel.Dispose();
moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, endMoveSound.Volume, endMoveSound.Range, item.WorldPosition);
if (moveSoundChannel != null) moveSoundChannel.Looping = false;
}
else if (!moveSoundChannel.IsPlaying)
{
moveSoundChannel.Dispose();
moveSoundChannel = null;
}
}
}
if (moveSoundChannel != null && moveSoundChannel.IsPlaying)
{
moveSoundChannel.Gain = MathHelper.Clamp(Math.Abs(angularVelocity), 0.5f, 1.0f);
}
if (flashLowPower || flashNoAmmo)
{
flashTimer += deltaTime;
if (flashTimer >= flashLength)
{
flashTimer = 0;
flashLowPower = false;
flashNoAmmo = false;
}
}
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (crosshairSprite != null)
{
Vector2 itemPos = cam.WorldToScreen(item.WorldPosition);
Vector2 turretDir = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
Vector2 mouseDiff = itemPos - PlayerInput.MousePosition;
crosshairPos = new Vector2(
MathHelper.Clamp(itemPos.X + turretDir.X * mouseDiff.Length(), 0, GameMain.GraphicsWidth),
MathHelper.Clamp(itemPos.Y + turretDir.Y * mouseDiff.Length(), 0, GameMain.GraphicsHeight));
}
crosshairPointerPos = PlayerInput.MousePosition;
}
public void Draw(SpriteBatch spriteBatch, bool editing = false)
{
Vector2 drawPos = new Vector2(item.Rect.X, item.Rect.Y);
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
if (item.Submarine != null) drawPos += item.Submarine.DrawPosition;
drawPos.Y = -drawPos.Y;
if (barrelSprite != null)
float recoilOffset = 0.0f;
if (RecoilDistance > 0.0f && recoilTimer > 0.0f)
{
barrelSprite.Draw(spriteBatch,
drawPos + barrelPos, Color.White,
rotation + MathHelper.PiOver2, 1.0f,
SpriteEffects.None, item.Sprite.Depth + 0.01f);
//move the barrel backwards 0.1 seconds after launching
if (recoilTimer >= Math.Max(Reload, 0.1f) - 0.1f)
{
recoilOffset = RecoilDistance * (1.0f - (recoilTimer - (Math.Max(Reload, 0.1f) - 0.1f)) / 0.1f);
}
//move back to normal position while reloading
else
{
recoilOffset = RecoilDistance * recoilTimer / (Math.Max(Reload, 0.1f) - 0.1f);
}
}
railSprite?.Draw(spriteBatch,
drawPos,
Color.White,
rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
barrelSprite?.Draw(spriteBatch,
drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale,
Color.White,
rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
if (!editing) return;
GUI.DrawLine(spriteBatch,
drawPos + barrelPos,
drawPos + barrelPos + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * 60.0f,
drawPos,
drawPos + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * 60.0f,
Color.Green);
GUI.DrawLine(spriteBatch,
drawPos + barrelPos,
drawPos + barrelPos + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * 60.0f,
drawPos,
drawPos + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * 60.0f,
Color.Green);
GUI.DrawLine(spriteBatch,
drawPos + barrelPos,
drawPos + barrelPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * 60.0f,
drawPos,
drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * 60.0f,
Color.LightGreen);
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
if (HudTint.A > 0)
{
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
new Color(HudTint.R, HudTint.G, HudTint.B) * (HudTint.A/255.0f), true);
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
new Color(HudTint.R, HudTint.G, HudTint.B) * (HudTint.A / 255.0f), true);
}
GetAvailablePower(out float batteryCharge, out float batteryCapacity);
float batteryCharge;
float batteryCapacity;
GetAvailablePower(out batteryCharge, out batteryCapacity);
List<Projectile> projectiles = GetLoadedProjectiles(false, true);
List<Item> availableAmmo = new List<Item>();
foreach (MapEntity e in item.linkedTo)
{
var linkedItem = e as Item;
if (linkedItem == null) continue;
var itemContainer = linkedItem.GetComponent<ItemContainer>();
if (itemContainer?.Inventory?.Items == null) continue;
availableAmmo.AddRange(itemContainer.Inventory.Items);
}
float chargeRate = powerConsumption <= 0.0f ? 1.0f : batteryCharge / batteryCapacity;
bool charged = batteryCharge * 3600.0f > powerConsumption;
bool readyToFire = reload <= 0.0f && charged && projectiles.Any(p => p != null);
bool readyToFire = reload <= 0.0f && charged && availableAmmo.Any(p => p != null);
if (ShowChargeIndicator && PowerConsumption > 0.0f)
{
powerIndicator.BarSize = chargeRate;
powerIndicator.Color = charged ? Color.Green : Color.Red;
powerIndicator.Draw(spriteBatch);
if (flashLowPower)
{
powerIndicator.BarSize = 1;
powerIndicator.Color *= (float)Math.Sin(flashTimer * 12);
powerIndicator.RectTransform.ChangeScale(Vector2.Lerp(Vector2.One, Vector2.One * 1.01f, 2 * (float)Math.Sin(flashTimer * 15)));
}
else
{
powerIndicator.BarSize = chargeRate;
}
powerIndicator.DrawManually(spriteBatch, true);
int requiredChargeIndicatorPos = (int)((powerConsumption / (batteryCapacity * 3600.0f)) * powerIndicator.Rect.Width);
GUI.DrawRectangle(spriteBatch,
@@ -129,27 +296,33 @@ namespace Barotrauma.Items.Components
{
Point slotSize = new Point(60, 30);
int spacing = 5;
int slotsPerRow = Math.Min(projectiles.Count, 6);
int slotsPerRow = Math.Min(availableAmmo.Count, 6);
int totalWidth = slotSize.X * slotsPerRow + spacing * (slotsPerRow - 1);
Point invSlotPos = new Point(GameMain.GraphicsWidth / 2 - totalWidth / 2, 60);
for (int i = 0; i < projectiles.Count; i++)
for (int i = 0; i < availableAmmo.Count; i++)
{
Inventory.DrawSlot(spriteBatch,
// TODO: Optimize? Creates multiple new classes per frame?
Inventory.DrawSlot(spriteBatch, null,
new InventorySlot(new Rectangle(invSlotPos + new Point((i % slotsPerRow) * (slotSize.X + spacing), (int)Math.Floor(i / (float)slotsPerRow) * (slotSize.Y + spacing)), slotSize)),
projectiles[i] == null ? null : projectiles[i].Item, true);
availableAmmo[i], true);
}
if (flashNoAmmo)
{
Rectangle rect = new Rectangle(invSlotPos.X, invSlotPos.Y, totalWidth, slotSize.Y);
float inflate = MathHelper.Lerp(3, 8, (float)Math.Abs(1 * Math.Sin(flashTimer * 5)));
rect.Inflate(inflate, inflate);
Color color = Color.Red * MathHelper.Max(0.5f, (float)Math.Sin(flashTimer * 12));
GUI.DrawRectangle(spriteBatch, rect, color, thickness: 3);
}
}
if (readyToFire)
if (crosshairSprite != null)
{
if (crosshairSprite != null) crosshairSprite.Draw(spriteBatch, PlayerInput.MousePosition);
}
else
{
if (disabledCrossHairSprite != null) disabledCrossHairSprite.Draw(spriteBatch, PlayerInput.MousePosition);
crosshairSprite.Draw(spriteBatch, crosshairPos, readyToFire ? Color.White : Color.White * 0.2f, 0, (float)Math.Sqrt(cam.Zoom));
}
if (crosshairPointerSprite != null) crosshairPointerSprite.Draw(spriteBatch, crosshairPointerPos, 0, (float)Math.Sqrt(cam.Zoom));
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
UInt16 projectileID = msg.ReadUInt16();
@@ -22,7 +22,7 @@ namespace Barotrauma.Items.Components
{
drawPos.Y -= rect.Height / 2;
if (dockingDir == 1)
if (DockingDir == 1)
{
spriteBatch.Draw(overlaySprite.Texture,
drawPos,
@@ -44,7 +44,7 @@ namespace Barotrauma.Items.Components
{
drawPos.X -= rect.Width / 2;
if (dockingDir == 1)
if (DockingDir == 1)
{
spriteBatch.Draw(overlaySprite.Texture,
drawPos - Vector2.UnitY * (rect.Height / 2 * dockingState),
@@ -74,7 +74,7 @@ namespace Barotrauma.Items.Components
body.FixtureList[0].GetAABB(out AABB aabb, 0);
Vector2 bodyDrawPos = ConvertUnits.ToDisplayUnits(new Vector2(aabb.LowerBound.X, aabb.UpperBound.Y));
if ((i == 1 || i == 3) && dockingTarget?.item?.Submarine != null) bodyDrawPos += dockingTarget.item.Submarine.Position;
if ((i == 1 || i == 3) && DockingTarget?.item?.Submarine != null) bodyDrawPos += DockingTarget.item.Submarine.Position;
if ((i == 0 || i == 2) && item.Submarine != null) bodyDrawPos += item.Submarine.Position;
bodyDrawPos.Y = -bodyDrawPos.Y;
@@ -1,187 +0,0 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma
{
partial class FixRequirement
{
private static GUIFrame frame;
public bool CanBeFixed(Character character, GUIComponent reqFrame = null)
{
foreach (string itemName in requiredItems)
{
Item item = character.Inventory.FindItem(itemName);
bool itemFound = (item != null);
if (reqFrame != null)
{
GUIComponent component = reqFrame.children.Find(c => c.UserData as string == itemName);
GUITextBlock text = component as GUITextBlock;
if (text != null) text.TextColor = itemFound ? Color.LightGreen : Color.Red;
}
}
foreach (Skill skill in requiredSkills)
{
float characterSkill = character.GetSkillLevel(skill.Name);
bool sufficientSkill = characterSkill >= skill.Level;
if (reqFrame != null)
{
GUIComponent component = reqFrame.children.Find(c => c.UserData as Skill == skill);
GUITextBlock text = component as GUITextBlock;
if (text != null) text.TextColor = sufficientSkill ? Color.LightGreen : Color.Red;
}
}
return CanBeFixed(character);
}
private static void CreateGUIFrame(Item item)
{
int width = 400, height = 500;
int y = 0;
frame = new GUIFrame(new Rectangle(0, 0, width, height), null, Alignment.Center, "");
frame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
frame.UserData = item;
new GUITextBlock(new Rectangle(0, 0, 200, 20), TextManager.Get("FixHeader").Replace("[itemname]", item.Name), "", frame);
y = y + 40;
foreach (FixRequirement requirement in item.FixRequirements)
{
GUIFrame reqFrame = new GUIFrame(
new Rectangle(0, y, 0, 20 + Math.Max(requirement.requiredItems.Count, requirement.requiredSkills.Count) * 15),
Color.Transparent, null, frame);
reqFrame.UserData = requirement;
var fixButton = new GUIButton(new Rectangle(0, 0, 50, 20), TextManager.Get("FixButton"), "", reqFrame);
fixButton.OnClicked = FixButtonPressed;
fixButton.UserData = requirement;
var tickBox = new GUITickBox(new Rectangle(70, 0, 20, 20), requirement.name, Alignment.Left, reqFrame);
tickBox.Enabled = false;
int y2 = 20;
foreach (string itemName in requirement.requiredItems)
{
var itemBlock = new GUITextBlock(new Rectangle(30, y2, 200, 15), itemName, "", reqFrame);
itemBlock.Font = GUI.SmallFont;
itemBlock.UserData = itemName;
y2 += 15;
}
y2 = 20;
foreach (Skill skill in requirement.requiredSkills)
{
var skillBlock = new GUITextBlock(new Rectangle(0, y2, 200, 15), skill.Name + " - " + skill.Level, "", Alignment.Right, Alignment.TopLeft, reqFrame);
skillBlock.Font = GUI.SmallFont;
skillBlock.UserData = skill;
y2 += 15;
}
y += reqFrame.Rect.Height;
}
}
private static bool FixButtonPressed(GUIButton button, object obj)
{
FixRequirement requirement = obj as FixRequirement;
if (requirement == null) return true;
Item item = frame.UserData as Item;
if (item == null) return true;
if (!requirement.CanBeFixed(Character.Controlled, button.Parent)) return true;
if (GameMain.Client != null)
{
GameMain.Client.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Repair, item.FixRequirements.IndexOf(requirement) });
}
else if (GameMain.Server != null)
{
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Status });
requirement.Fixed = true;
}
else
{
requirement.Fixed = true;
}
return true;
}
private static void UpdateGUIFrame(Item item, Character character)
{
if (frame == null) return;
bool unfixedFound = false;
foreach (GUIComponent child in frame.children)
{
FixRequirement requirement = child.UserData as FixRequirement;
if (requirement == null) continue;
if (requirement.Fixed)
{
child.Color = Color.LightGreen * 0.3f;
child.GetChild<GUITickBox>().Selected = true;
}
else
{
bool canBeFixed = requirement.CanBeFixed(character, child);
unfixedFound = true;
//child.GetChild<GUITickBox>().Selected = canBeFixed;
GUITickBox tickBox = child.GetChild<GUITickBox>();
if (tickBox.Selected)
{
tickBox.Selected = canBeFixed;
requirement.Fixed = canBeFixed;
}
child.Color = Color.Red * 0.2f;
//tickBox.State = GUIComponent.ComponentState.None;
}
}
if (!unfixedFound)
{
item.Condition = item.Prefab.Health;
frame = null;
}
}
public static void DrawHud(SpriteBatch spriteBatch, Item item, Character character)
{
if (frame == null) return;
frame.Draw(spriteBatch);
}
public static void AddToGUIUpdateList()
{
if (frame == null) return;
frame.AddToGUIUpdateList();
}
public static void UpdateHud(Item item, Character character)
{
if (frame == null || frame.UserData != item)
{
CreateGUIFrame(item);
}
UpdateGUIFrame(item, character);
if (frame == null) return;
frame.Update((float)Timing.Step);
}
}
}
File diff suppressed because it is too large Load Diff
+538 -176
View File
@@ -8,26 +8,68 @@ using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
partial class Item : MapEntity, IDamageable, ISerializableEntity, IServerSerializable, IClientSerializable
{
public static bool ShowItems = true;
private List<ItemComponent> activeHUDs = new List<ItemComponent>();
public IEnumerable<ItemComponent> ActiveHUDs => activeHUDs;
class SpriteState
{
public float RotationState;
public float OffsetState;
public bool IsActive = true;
}
private Dictionary<ItemPrefab.DecorativeSprite, SpriteState> spriteAnimState = new Dictionary<ItemPrefab.DecorativeSprite, SpriteState>();
private Sprite activeSprite;
public override Sprite Sprite
{
get { return prefab.GetActiveSprite(condition); }
get { return activeSprite; }
}
private GUITextBlock itemInUseWarning;
private GUITextBlock ItemInUseWarning
{
get
{
if (itemInUseWarning == null)
{
itemInUseWarning = new GUITextBlock(new RectTransform(new Point(10), GUI.Canvas), "",
textColor: Color.Orange, color: Color.Black,
textAlignment:Alignment.Center, style: "OuterGlow");
}
return itemInUseWarning;
}
}
public override bool SelectableInEditor
{
get
{
return parentInventory == null && (body == null || body.Enabled) && ShowItems;
}
}
public float SpriteRotation;
public Color GetSpriteColor()
{
Color color = spriteColor;
if (prefab.UseContainedSpriteColor && ownInventory != null)
if (Prefab.UseContainedSpriteColor && ownInventory != null)
{
for (int i = 0; i < ownInventory.Items.Length; i++)
{
if (ownInventory.Items[i] != null)
{
color = ownInventory.Items[i].spriteColor;
color = ownInventory.Items[i].ContainerColor;
break;
}
}
@@ -35,45 +77,94 @@ namespace Barotrauma
return color;
}
public Color GetInventoryIconColor()
{
Color color = InventoryIconColor;
if (Prefab.UseContainedInventoryIconColor && ownInventory != null)
{
for (int i = 0; i < ownInventory.Items.Length; i++)
{
if (ownInventory.Items[i] != null)
{
color = ownInventory.Items[i].ContainerColor;
break;
}
}
}
return color;
}
partial void SetActiveSprite()
{
activeSprite = prefab.sprite;
if (Container != null)
{
foreach (ContainedItemSprite containedSprite in Prefab.ContainedSprites)
{
if (containedSprite.MatchesContainer(Container))
{
activeSprite = containedSprite.Sprite;
return;
}
}
}
foreach (BrokenItemSprite brokenSprite in Prefab.BrokenSprites)
{
if (condition <= brokenSprite.MaxCondition)
{
activeSprite = brokenSprite.Sprite;
break;
}
}
}
partial void InitProjSpecific()
{
foreach (var decorativeSprite in ((ItemPrefab)prefab).DecorativeSprites)
{
spriteAnimState.Add(decorativeSprite, new SpriteState());
}
}
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (!Visible) return;
if (!Visible || (!editing && hiddenInGame)) return; // TODO: Prevent drawing hiddenInGame objects via cheating with server-side checks
if (editing && !ShowItems) return;
Color color = (IsSelected && editing) ? Color.Red : GetSpriteColor();
if (isHighlighted) color = Color.Orange;
Color color = isHighlighted ? Color.Orange : GetSpriteColor();
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
Sprite activeSprite = prefab.sprite;
BrokenItemSprite fadeInBrokenSprite = null;
float fadeInBrokenSpriteAlpha = 0.0f;
if (condition < 100.0f)
{
for (int i = 0; i < prefab.BrokenSprites.Count; i++)
for (int i = 0; i < Prefab.BrokenSprites.Count; i++)
{
if (condition <= prefab.BrokenSprites[i].MaxCondition)
if (condition <= Prefab.BrokenSprites[i].MaxCondition)
{
activeSprite = prefab.BrokenSprites[i].Sprite;
activeSprite = Prefab.BrokenSprites[i].Sprite;
break;
}
if (prefab.BrokenSprites[i].FadeIn)
if (Prefab.BrokenSprites[i].FadeIn)
{
float min = i > 0 ? prefab.BrokenSprites[i].MaxCondition : 0.0f;
float max = i < prefab.BrokenSprites.Count - 1 ? prefab.BrokenSprites[i + 1].MaxCondition : 100.0f;
float min = i > 0 ? Prefab.BrokenSprites[i].MaxCondition : 0.0f;
float max = i < Prefab.BrokenSprites.Count - 1 ? Prefab.BrokenSprites[i + 1].MaxCondition : 100.0f;
fadeInBrokenSpriteAlpha = 1.0f - ((condition - min) / (max - min));
if (fadeInBrokenSpriteAlpha > 0.0f && fadeInBrokenSpriteAlpha < 1.0f)
{
fadeInBrokenSprite = prefab.BrokenSprites[i];
fadeInBrokenSprite = Prefab.BrokenSprites[i];
}
}
}
}
Sprite selectedSprite = prefab.GetActiveSprite(condition);
if (selectedSprite != null)
if (activeSprite != null)
{
SpriteEffects oldEffects = selectedSprite.effects;
selectedSprite.effects ^= SpriteEffects;
SpriteEffects oldEffects = activeSprite.effects;
activeSprite.effects ^= SpriteEffects;
SpriteEffects oldBrokenSpriteEffects = SpriteEffects.None;
if (fadeInBrokenSprite != null)
{
@@ -84,17 +175,38 @@ namespace Barotrauma
float depth = GetDrawDepth();
if (body == null)
{
if (prefab.ResizeHorizontal || prefab.ResizeVertical || SpriteEffects.HasFlag(SpriteEffects.FlipHorizontally) || SpriteEffects.HasFlag(SpriteEffects.FlipVertically))
{
selectedSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), color: color);
fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), color: color * fadeInBrokenSpriteAlpha,
depth: selectedSprite.Depth - 0.000001f);
bool flipHorizontal = (SpriteEffects & SpriteEffects.FlipHorizontally) != 0;
bool flipVertical = (SpriteEffects & SpriteEffects.FlipVertically) != 0;
if (prefab.ResizeHorizontal || prefab.ResizeVertical)
{
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), color: color,
depth: depth);
fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), color: color * fadeInBrokenSpriteAlpha,
depth: depth - 0.000001f);
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
new Vector2(rect.Width, rect.Height), color: color,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
else
{
selectedSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color, 0.0f, 1.0f, SpriteEffects.None, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color * fadeInBrokenSpriteAlpha, 0.0f, 1.0f, SpriteEffects.None, depth - 0.000001f);
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color, SpriteRotation, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color * fadeInBrokenSpriteAlpha, SpriteRotation, Scale, activeSprite.effects, depth - 0.000001f);
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
SpriteRotation + rotation, Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
}
else if (body.Enabled)
@@ -105,7 +217,7 @@ namespace Barotrauma
if (holdable.Picker.SelectedItems[0] == this)
{
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand);
depth = holdLimb.sprite.Depth + 0.000001f;
depth = holdLimb.ActiveSprite.Depth + 0.000001f;
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) depth = Math.Min(wearableSprite.Sprite.Depth, depth);
@@ -114,42 +226,70 @@ namespace Barotrauma
else if (holdable.Picker.SelectedItems[1] == this)
{
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand);
depth = holdLimb.sprite.Depth - 0.000001f;
depth = holdLimb.ActiveSprite.Depth - 0.000001f;
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
{
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) depth = Math.Max(wearableSprite.Sprite.Depth, depth);
}
}
}
body.Draw(spriteBatch, selectedSprite, color, depth);
if (fadeInBrokenSprite != null) body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, depth - 0.000001f);
body.Draw(spriteBatch, activeSprite, color, depth, Scale);
if (fadeInBrokenSprite != null) body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, depth - 0.000001f, Scale);
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + transformedOffset.X, -(DrawPosition.Y + transformedOffset.Y)), color,
-body.Rotation + rotation, Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
selectedSprite.effects = oldEffects;
activeSprite.effects = oldEffects;
if (fadeInBrokenSprite != null)
{
fadeInBrokenSprite.Sprite.effects = oldEffects;
}
}
List<IDrawableComponent> staticDrawableComponents = new List<IDrawableComponent>(drawableComponents); //static list to compensate for drawable toggling
for (int i = 0; i < staticDrawableComponents.Count; i++)
//use a backwards for loop because the drawable components may disable drawing,
//causing them to be removed from the list
for (int i = drawableComponents.Count - 1; i >= 0; i--)
{
staticDrawableComponents[i].Draw(spriteBatch, editing);
drawableComponents[i].Draw(spriteBatch, editing);
}
if (GameMain.DebugDraw && aiTarget != null) aiTarget.Draw(spriteBatch);
if (GameMain.DebugDraw)
{
aiTarget?.Draw(spriteBatch);
var containedItems = ContainedItems;
if (containedItems != null)
{
foreach (Item item in containedItems)
{
item.AiTarget?.Draw(spriteBatch);
}
}
}
if (!editing || (body != null && !body.Enabled))
{
return;
}
if (IsSelected || isHighlighted)
if (IsSelected || IsHighlighted)
{
GUI.DrawRectangle(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), Color.Green, false, 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
GUI.DrawRectangle(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height),
Color.White, false, 0, thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
foreach (Rectangle t in prefab.Triggers)
foreach (Rectangle t in Prefab.Triggers)
{
Rectangle transformedTrigger = TransformTrigger(t);
@@ -171,13 +311,82 @@ namespace Barotrauma
foreach (MapEntity e in linkedTo)
{
GUI.DrawLine(spriteBatch,
new Vector2(WorldPosition.X, -WorldPosition.Y),
new Vector2(e.WorldPosition.X, -e.WorldPosition.Y),
Color.Red * 0.3f);
bool isLinkAllowed = prefab.IsLinkAllowed(e.prefab);
Color lineColor = Color.Red * 0.5f;
if (isLinkAllowed)
{
lineColor = e is Item i && (DisplaySideBySideWhenLinked || i.DisplaySideBySideWhenLinked) ? Color.Purple * 0.5f : Color.LightGreen * 0.5f;
}
Vector2 from = new Vector2(WorldPosition.X, -WorldPosition.Y);
Vector2 to = new Vector2(e.WorldPosition.X, -e.WorldPosition.Y);
GUI.DrawLine(spriteBatch, from, to, lineColor * 0.25f, width: 3);
GUI.DrawLine(spriteBatch, from, to, lineColor, width: 1);
//GUI.DrawString(spriteBatch, from, $"Linked to {e.Name}", lineColor, Color.Black * 0.5f);
}
}
public void UpdateSpriteStates(float deltaTime)
{
foreach (int spriteGroup in Prefab.DecorativeSpriteGroups.Keys)
{
for (int i = 0; i < Prefab.DecorativeSpriteGroups[spriteGroup].Count; i++)
{
var decorativeSprite = Prefab.DecorativeSpriteGroups[spriteGroup][i];
if (decorativeSprite == null) { continue; }
if (spriteGroup > 0)
{
int activeSpriteIndex = ID % Prefab.DecorativeSpriteGroups[spriteGroup].Count;
if (i != activeSpriteIndex)
{
spriteAnimState[decorativeSprite].IsActive = false;
continue;
}
}
//check if the sprite is active (whether it should be drawn or not)
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = true;
foreach (PropertyConditional conditional in decorativeSprite.IsActiveConditionals)
{
if (!ConditionalMatches(conditional))
{
spriteState.IsActive = false;
break;
}
}
if (!spriteState.IsActive) { continue; }
//check if the sprite should be animated
bool animate = true;
foreach (PropertyConditional conditional in decorativeSprite.AnimationConditionals)
{
if (!ConditionalMatches(conditional)) { animate = false; break; }
}
if (!animate) { continue; }
spriteState.OffsetState += deltaTime;
spriteState.RotationState += deltaTime;
bool ConditionalMatches(PropertyConditional conditional)
{
if (string.IsNullOrEmpty(conditional.TargetItemComponentName))
{
if (!conditional.Matches(this)) { return false; }
}
else
{
foreach (ItemComponent component in components)
{
if (component.Name != conditional.TargetItemComponentName) { continue; }
if (!conditional.Matches(component)) { return false; }
}
}
return true;
}
}
}
}
public override void UpdateEditing(Camera cam)
{
if (editingHUD == null || editingHUD.UserData as Item != this)
@@ -185,10 +394,10 @@ namespace Barotrauma
editingHUD = CreateEditingHUD(Screen.Selected != GameMain.SubEditorScreen);
}
editingHUD.Update((float)Timing.Step);
if (Screen.Selected != GameMain.SubEditorScreen) return;
if (Character.Controlled == null) activeHUDs.Clear();
if (!Linkable) return;
if (!PlayerInput.KeyDown(Keys.Space)) return;
@@ -197,175 +406,313 @@ namespace Barotrauma
if (!lClick && !rClick) return;
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
if (lClick)
var otherEntity = mapEntityList.FirstOrDefault(e => e != this && e.IsHighlighted && e.IsMouseOn(position));
if (otherEntity != null)
{
foreach (MapEntity entity in mapEntityList)
if (linkedTo.Contains(otherEntity))
{
if (entity == this || !entity.IsHighlighted) continue;
if (linkedTo.Contains(entity)) continue;
if (!entity.IsMouseOn(position)) continue;
linkedTo.Add(entity);
if (entity.Linkable && entity.linkedTo != null) entity.linkedTo.Add(this);
linkedTo.Remove(otherEntity);
if (otherEntity.linkedTo != null && otherEntity.linkedTo.Contains(this)) otherEntity.linkedTo.Remove(this);
}
}
else
{
foreach (MapEntity entity in mapEntityList)
else
{
if (entity == this || !entity.IsHighlighted) continue;
if (!linkedTo.Contains(entity)) continue;
if (!entity.IsMouseOn(position)) continue;
linkedTo.Remove(entity);
if (entity.linkedTo != null && entity.linkedTo.Contains(this)) entity.linkedTo.Remove(this);
linkedTo.Add(otherEntity);
if (otherEntity.Linkable && otherEntity.linkedTo != null) otherEntity.linkedTo.Add(this);
}
}
}
public override void DrawEditing(SpriteBatch spriteBatch, Camera cam)
{
if (editingHUD != null && editingHUD.UserData == this) editingHUD.Draw(spriteBatch);
}
private GUIComponent CreateEditingHUD(bool inGame = false)
{
int width = 450;
int height = 150;
int x = GameMain.GraphicsWidth / 2 - width / 2, y = 30;
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null)
{
Spacing = 5
};
var itemEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true);
editingHUD = new GUIListBox(new Rectangle(x, y, width, height), "");
editingHUD.UserData = this;
GUIListBox listBox = (GUIListBox)editingHUD;
listBox.Spacing = 5;
var itemEditor = new SerializableEntityEditor(this, inGame, editingHUD, true);
if (!inGame && Linkable)
{
itemEditor.AddCustomContent(new GUITextBlock(new Rectangle(0, 0, 0, 20), "Hold space to link to another item", "", null, GUI.SmallFont), 1);
var linkText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, 20)), TextManager.Get("HoldToLink"), font: GUI.SmallFont);
var itemsText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, 20)), TextManager.Get("AllowedLinks") + ": ", font: GUI.SmallFont);
if (AllowedLinks.None())
{
itemsText.Text += TextManager.Get("None");
}
else
{
for (int i = 0; i < AllowedLinks.Count; i++)
{
itemsText.Text += AllowedLinks[i];
if (i < AllowedLinks.Count - 1)
{
itemsText.Text += ", ";
}
}
}
itemEditor.AddCustomContent(linkText, 1);
itemEditor.AddCustomContent(itemsText, 2);
linkText.TextColor = Color.Yellow;
itemsText.TextColor = Color.Yellow;
}
if (!inGame && Sprite != null)
{
var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), "Reload Texture");
reloadTextureButton.OnClicked += (button, data) =>
{
Sprite.ReloadTexture();
Sprite.ReloadXML();
return true;
};
itemEditor.AddCustomContent(reloadTextureButton, itemEditor.ContentCount);
}
foreach (ItemComponent ic in components)
{
if (ic.requiredItems.Count == 0)
if (inGame)
{
if (inGame)
{
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0) continue;
}
else
{
if (SerializableProperty.GetProperties<Editable>(ic).Count == 0) continue;
}
if (!ic.AllowInGameEditing) continue;
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0) continue;
}
else
{
if (ic.requiredItems.Count == 0 && SerializableProperty.GetProperties<Editable>(ic).Count == 0) continue;
}
var componentEditor = new SerializableEntityEditor(ic, inGame, editingHUD, !inGame);
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame, showName: !inGame);
if (inGame) continue;
foreach (RelatedItem relatedItem in ic.requiredItems)
foreach (var kvp in ic.requiredItems)
{
var textBlock = new GUITextBlock(new Rectangle(0, 0, 0, 20), relatedItem.Type.ToString() + " required", "", Alignment.TopLeft, Alignment.CenterLeft, null, false, GUI.SmallFont);
textBlock.Padding = new Vector4(10.0f, 0.0f, 10.0f, 0.0f);
componentEditor.AddCustomContent(textBlock, 1);
GUITextBox namesBox = new GUITextBox(new Rectangle(0, 0, 180, 20), Alignment.Right, "", textBlock);
namesBox.Font = GUI.SmallFont;
namesBox.Text = relatedItem.JoinedNames;
namesBox.OnDeselected += (textBox, key) =>
foreach (RelatedItem relatedItem in kvp.Value)
{
relatedItem.JoinedNames = textBox.Text;
textBox.Text = relatedItem.JoinedNames;
};
var textBlock = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, 20)),
relatedItem.Type.ToString() + " required", font: GUI.SmallFont)
{
Padding = new Vector4(10.0f, 0.0f, 10.0f, 0.0f)
};
componentEditor.AddCustomContent(textBlock, 1);
namesBox.OnEnterPressed += (textBox, text) =>
{
relatedItem.JoinedNames = text;
textBox.Text = relatedItem.JoinedNames;
return true;
};
GUITextBox namesBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), textBlock.RectTransform, Anchor.CenterRight))
{
Font = GUI.SmallFont,
Text = relatedItem.JoinedIdentifiers
};
y += 20;
namesBox.OnDeselected += (textBox, key) =>
{
relatedItem.JoinedIdentifiers = textBox.Text;
textBox.Text = relatedItem.JoinedIdentifiers;
};
namesBox.OnEnterPressed += (textBox, text) =>
{
relatedItem.JoinedIdentifiers = text;
textBox.Text = relatedItem.JoinedIdentifiers;
return true;
};
}
}
}
int contentHeight = (int)(editingHUD.children.Sum(c => c.Rect.Height) + (listBox.children.Count - 1) * listBox.Spacing + listBox.Padding.Y + listBox.Padding.W);
editingHUD.SetDimensions(new Point(editingHUD.Rect.Width, MathHelper.Clamp(contentHeight, 50, editingHUD.Rect.Height)));
PositionEditingHUD();
SetHUDLayout();
return editingHUD;
}
public virtual void UpdateHUD(Camera cam, Character character)
/// <summary>
/// Reposition currently active item interfaces to make sure they don't overlap with each other
/// </summary>
private void SetHUDLayout()
{
if (condition <= 0.0f)
//reset positions first
List<GUIComponent> elementsToMove = new List<GUIComponent>();
if (editingHUD != null && editingHUD.UserData == this)
{
FixRequirement.UpdateHud(this, character);
return;
elementsToMove.Add(editingHUD);
}
foreach (ItemComponent ic in activeHUDs)
{
if (ic.GuiFrame == null || ic.AllowUIOverlap || ic.GetLinkUIToComponent() != null) continue;
ic.GuiFrame.RectTransform.ScreenSpaceOffset = Point.Zero;
elementsToMove.Add(ic.GuiFrame);
}
if (HasInGameEditableProperties)
List<Rectangle> disallowedAreas = new List<Rectangle>();
if (GameMain.GameSession?.CrewManager != null)
{
UpdateEditing(cam);
disallowedAreas.Add(GameMain.GameSession.CrewManager.GetCharacterListArea());
}
foreach (ItemComponent ic in components)
GUI.PreventElementOverlap(elementsToMove, disallowedAreas,
new Rectangle(
20, 20,
GameMain.GraphicsWidth - 40,
HUDLayoutSettings.InventoryTopY > 0 ? HUDLayoutSettings.InventoryTopY - 20 : GameMain.GraphicsHeight - 80));
foreach (ItemComponent ic in activeHUDs)
{
if (ic.CanBeSelected) ic.UpdateHUD(character);
if (ic.GuiFrame == null) continue;
var linkUIToComponent = ic.GetLinkUIToComponent();
if (linkUIToComponent == null) continue;
ic.GuiFrame.RectTransform.ScreenSpaceOffset = linkUIToComponent.GuiFrame.RectTransform.ScreenSpaceOffset;
}
}
public virtual void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
public void UpdateHUD(Camera cam, Character character, float deltaTime)
{
if (condition <= 0.0f)
bool editingHUDCreated = false;
if (HasInGameEditableProperties ||
Screen.Selected == GameMain.SubEditorScreen)
{
FixRequirement.DrawHud(spriteBatch, this, character);
return;
UpdateEditing(cam);
editingHUDCreated = editingHUD != null;
}
List<ItemComponent> prevActiveHUDs = new List<ItemComponent>(activeHUDs);
List<ItemComponent> activeComponents = new List<ItemComponent>(components);
foreach (MapEntity entity in linkedTo)
{
if (prefab.IsLinkAllowed(entity.prefab) && entity is Item i)
{
if (!i.DisplaySideBySideWhenLinked) continue;
activeComponents.AddRange(i.components);
}
}
activeHUDs.Clear();
//the HUD of the component with the highest priority will be drawn
//if all components have a priority of 0, all of them are drawn
List<ItemComponent> maxPriorityHUDs = new List<ItemComponent>();
foreach (ItemComponent ic in activeComponents)
{
if (ic.CanBeSelected && ic.HudPriority > 0 && ic.ShouldDrawHUD(character) &&
(maxPriorityHUDs.Count == 0 || ic.HudPriority >= maxPriorityHUDs[0].HudPriority))
{
if (maxPriorityHUDs.Count > 0 && ic.HudPriority > maxPriorityHUDs[0].HudPriority) maxPriorityHUDs.Clear();
maxPriorityHUDs.Add(ic);
}
}
if (maxPriorityHUDs.Count > 0)
{
activeHUDs.AddRange(maxPriorityHUDs);
}
else
{
foreach (ItemComponent ic in activeComponents)
{
if (ic.CanBeSelected && ic.ShouldDrawHUD(character)) activeHUDs.Add(ic);
}
}
//active HUDs have changed, need to reposition
if (!prevActiveHUDs.SequenceEqual(activeHUDs) || editingHUDCreated)
{
SetHUDLayout();
}
Rectangle mergedHUDRect = Rectangle.Empty;
foreach (ItemComponent ic in activeHUDs)
{
ic.UpdateHUD(character, deltaTime, cam);
if (ic.GuiFrame != null && ic.GuiFrame.Rect.Height < GameMain.GraphicsHeight)
{
mergedHUDRect = mergedHUDRect == Rectangle.Empty ?
ic.GuiFrame.Rect :
Rectangle.Union(mergedHUDRect, ic.GuiFrame.Rect);
}
}
if (itemInUseWarning != null && mergedHUDRect != Rectangle.Empty)
{
itemInUseWarning.Visible = false;
foreach (Character otherCharacter in Character.CharacterList)
{
if (otherCharacter != character &&
otherCharacter.SelectedConstruction == character.SelectedConstruction)
{
ItemInUseWarning.Visible = true;
if (mergedHUDRect.Width > GameMain.GraphicsWidth / 2) { mergedHUDRect.Inflate(-GameMain.GraphicsWidth / 4, 0); }
itemInUseWarning.RectTransform.ScreenSpaceOffset = new Point(mergedHUDRect.X, mergedHUDRect.Bottom);
itemInUseWarning.RectTransform.NonScaledSize = new Point(mergedHUDRect.Width, (int)(50 * GUI.Scale));
if (itemInUseWarning.UserData != otherCharacter)
{
itemInUseWarning.Text = TextManager.Get("ItemInUse").Replace("[character]", otherCharacter.Name);
itemInUseWarning.UserData = otherCharacter;
}
break;
}
}
}
}
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
{
if (HasInGameEditableProperties)
{
DrawEditing(spriteBatch, cam);
}
foreach (ItemComponent ic in components)
foreach (ItemComponent ic in activeHUDs)
{
if (ic.CanBeSelected) ic.DrawHUD(spriteBatch, character);
if (ic.CanBeSelected)
{
ic.DrawHUD(spriteBatch, character);
}
}
}
public override void AddToGUIUpdateList()
{
AddToGUIUpdateList(addLinkedHUDs: true);
}
private void AddToGUIUpdateList(bool addLinkedHUDs)
{
if (Screen.Selected is SubEditorScreen)
{
if (editingHUD != null) editingHUD.AddToGUIUpdateList();
if (editingHUD != null && editingHUD.UserData == this) editingHUD.AddToGUIUpdateList();
}
else
{
if (HasInGameEditableProperties)
{
if (editingHUD != null) editingHUD.AddToGUIUpdateList();
if (editingHUD != null && editingHUD.UserData == this) editingHUD.AddToGUIUpdateList();
}
}
if (Character.Controlled != null && Character.Controlled.SelectedConstruction == this)
{
if (condition <= 0.0f)
{
FixRequirement.AddToGUIUpdateList();
return;
}
if (Character.Controlled != null && Character.Controlled?.SelectedConstruction != this) return;
foreach (ItemComponent ic in components)
{
if (ic.CanBeSelected) ic.AddToGUIUpdateList();
}
bool needsLayoutUpdate = false;
foreach (ItemComponent ic in activeHUDs)
{
if (!ic.CanBeSelected) { continue; }
bool useAlternativeLayout = ic.Item != this;
bool wasUsingAlternativeLayout = ic.UseAlternativeLayout;
ic.UseAlternativeLayout = useAlternativeLayout;
needsLayoutUpdate |= ic.UseAlternativeLayout != wasUsingAlternativeLayout;
ic.AddToGUIUpdateList();
}
if (itemInUseWarning != null && itemInUseWarning.Visible)
{
itemInUseWarning.AddToGUIUpdateList();
}
if (needsLayoutUpdate)
{
SetHUDLayout();
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
if (type == ServerNetObject.ENTITY_POSITION)
@@ -380,42 +727,50 @@ namespace Barotrauma
switch (eventType)
{
case NetEntityEvent.Type.ComponentState:
int componentIndex = msg.ReadRangedInteger(0, components.Count - 1);
(components[componentIndex] as IServerSerializable).ClientRead(type, msg, sendingTime);
{
int componentIndex = msg.ReadRangedInteger(0, components.Count - 1);
(components[componentIndex] as IServerSerializable).ClientRead(type, msg, sendingTime);
}
break;
case NetEntityEvent.Type.InventoryState:
int containerIndex = msg.ReadRangedInteger(0, components.Count - 1);
(components[containerIndex] as ItemContainer).Inventory.ClientRead(type, msg, sendingTime);
{
int containerIndex = msg.ReadRangedInteger(0, components.Count - 1);
(components[containerIndex] as ItemContainer).Inventory.ClientRead(type, msg, sendingTime);
}
break;
case NetEntityEvent.Type.Status:
float prevCondition = condition;
condition = msg.ReadSingle();
if (FixRequirements.Count > 0)
{
if (Condition <= 0.0f)
{
for (int i = 0; i < FixRequirements.Count; i++)
FixRequirements[i].Fixed = msg.ReadBoolean();
}
else
{
for (int i = 0; i < FixRequirements.Count; i++)
FixRequirements[i].Fixed = true;
}
}
if (prevCondition > 0.0f && condition <= 0.0f)
{
ApplyStatusEffects(ActionType.OnBroken, 1.0f);
foreach (ItemComponent ic in components)
{
ic.PlaySound(ActionType.OnBroken, WorldPosition);
}
}
break;
case NetEntityEvent.Type.ApplyStatusEffect:
ActionType actionType = (ActionType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length - 1);
ushort targetID = msg.ReadUInt16();
{
ActionType actionType = (ActionType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length - 1);
byte componentIndex = msg.ReadByte();
ushort targetID = msg.ReadUInt16();
byte targetLimbID = msg.ReadByte();
Character target = FindEntityByID(targetID) as Character;
//ignore deltatime - using an item with the useOnSelf buttons is instantaneous
ApplyStatusEffects(actionType, 1.0f, target, true);
ItemComponent targetComponent = componentIndex < components.Count ? components[componentIndex] : null;
Character target = FindEntityByID(targetID) as Character;
Limb targetLimb = target != null && targetLimbID < target.AnimController.Limbs.Length ? target.AnimController.Limbs[targetLimbID] : null;
if (targetComponent == null)
{
ApplyStatusEffects(actionType, 1.0f, target, targetLimb, true);
}
else
{
targetComponent.ApplyStatusEffects(actionType, 1.0f, target, targetLimb);
}
}
break;
case NetEntityEvent.Type.ChangeProperty:
ReadPropertyChange(msg, false);
@@ -446,16 +801,14 @@ namespace Barotrauma
msg.WriteRangedInteger(0, components.Count - 1, containerIndex);
(components[containerIndex] as ItemContainer).Inventory.ClientWrite(msg, extraData);
break;
case NetEntityEvent.Type.Repair:
if (FixRequirements.Count > 0)
{
int requirementIndex = (int)extraData[1];
msg.WriteRangedInteger(0, FixRequirements.Count - 1, requirementIndex);
}
break;
case NetEntityEvent.Type.ApplyStatusEffect:
//no further data needed, the server applies the effect
//on the character of the client who sent the message
case NetEntityEvent.Type.Treatment:
UInt16 characterID = (UInt16)extraData[1];
Limb targetLimb = (Limb)extraData[2];
Character targetCharacter = FindEntityByID(characterID) as Character;
msg.Write(characterID);
msg.Write(targetCharacter == null ? (byte)255 : (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb));
break;
case NetEntityEvent.Type.ChangeProperty:
WritePropertyChange(msg, extraData, true);
@@ -538,5 +891,14 @@ namespace Barotrauma
GameMain.Client.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.ComponentState, index });
}
partial void RemoveProjSpecific()
{
if (Inventory.draggingItem == this)
{
Inventory.draggingItem = null;
Inventory.draggingSlot = null;
}
}
}
}
@@ -0,0 +1,107 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
namespace Barotrauma
{
partial class ItemInventory : Inventory
{
protected override void ControlInput(Camera cam)
{
if (draggingItem == null && HUD.CloseHUD(BackgroundFrame))
{
// TODO: fix so that works with the server side
Character.Controlled.SelectedConstruction = null;
return;
}
base.ControlInput(cam);
if (BackgroundFrame.Contains(PlayerInput.MousePosition))
{
cam.OffsetAmount = 0;
//if this is used, we need to implement syncing this inventory with the server
/*Character.DisableControls = true;
if (Character.Controlled != null)
{
if (PlayerInput.KeyHit(InputType.Select))
{
Character.Controlled.SelectedConstruction = null;
}
}*/
}
}
protected override void CalculateBackgroundFrame()
{
var firstSlot = slots.FirstOrDefault();
if (firstSlot == null) { return; }
Rectangle frame = firstSlot.Rect;
frame.Location += firstSlot.DrawOffset.ToPoint();
for (int i = 1; i < capacity; i++)
{
Rectangle slotRect = slots[i].Rect;
slotRect.Location += slots[i].DrawOffset.ToPoint();
frame = Rectangle.Union(frame, slotRect);
}
BackgroundFrame = new Rectangle(
frame.X - (int)padding.X,
frame.Y - (int)padding.Y,
frame.Width + (int)(padding.X + padding.Z),
frame.Height + (int)(padding.Y + padding.W));
}
public override void Draw(SpriteBatch spriteBatch, bool subInventory = false)
{
if (slots != null && slots.Length > 0)
{
CalculateBackgroundFrame();
if (container.InventoryBackSprite == null)
{
//draw a black baground for item inventories that don't have a RectTransform
//(= ItemContainers that have no GUIFrame or aren't a part of another component's UI)
if (RectTransform == null)
{
GUI.DrawRectangle(spriteBatch, BackgroundFrame, Color.Black * 0.8f, true);
}
}
else
{
container.InventoryBackSprite.Draw(
spriteBatch, BackgroundFrame.Location.ToVector2(),
Color.White, Vector2.Zero, 0,
new Vector2(
BackgroundFrame.Width / container.InventoryBackSprite.size.X,
BackgroundFrame.Height / container.InventoryBackSprite.size.Y));
}
base.Draw(spriteBatch, subInventory);
if (container.InventoryBottomSprite != null && !subInventory)
{
container.InventoryBottomSprite.Draw(spriteBatch,
new Vector2(BackgroundFrame.Center.X, BackgroundFrame.Bottom) + slots[0].DrawOffset,
0.0f, UIScale);
}
if (container.InventoryTopSprite == null)
{
Item item = Owner as Item;
string label = container.UILabel ?? item?.Name;
if (!string.IsNullOrEmpty(label) && !subInventory)
{
GUI.DrawString(spriteBatch,
new Vector2((int)(BackgroundFrame.Center.X - GUI.Font.MeasureString(label).X / 2), (int)BackgroundFrame.Y + 5),
label, Color.White * 0.9f);
}
}
else if (!subInventory)
{
container.InventoryTopSprite.Draw(spriteBatch, new Vector2(BackgroundFrame.Center.X, BackgroundFrame.Y), 0.0f, UIScale);
}
}
else
{
base.Draw(spriteBatch, subInventory);
}
}
}
}
@@ -2,6 +2,8 @@
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -20,22 +22,174 @@ namespace Barotrauma
}
}
class ContainedItemSprite
{
public readonly Sprite Sprite;
public readonly string[] AllowedContainerIdentifiers;
public readonly string[] AllowedContainerTags;
public ContainedItemSprite(XElement element, string path = "")
{
Sprite = new Sprite(element, path);
AllowedContainerIdentifiers = element.GetAttributeStringArray("allowedcontaineridentifiers", new string[0], convertToLowerInvariant: true);
AllowedContainerTags = element.GetAttributeStringArray("allowedcontainertags", new string[0], convertToLowerInvariant: true);
}
public bool MatchesContainer(Item container)
{
if (container == null) { return false; }
return AllowedContainerIdentifiers.Contains(container.prefab.Identifier) ||
AllowedContainerTags.Any(t => container.prefab.Tags.Contains(t));
}
}
partial class ItemPrefab : MapEntityPrefab
{
public List<BrokenItemSprite> BrokenSprites = new List<BrokenItemSprite>();
public Sprite GetActiveSprite(float condition)
public class DecorativeSprite
{
Sprite activeSprite = sprite;
foreach (BrokenItemSprite brokenSprite in BrokenSprites)
public Sprite Sprite { get; private set; }
public enum AnimationType
{
if (condition <= brokenSprite.MaxCondition)
None,
Sine,
Noise
}
[Serialize("0,0", false)]
public Vector2 Offset { get; private set; }
[Serialize(AnimationType.None, false)]
public AnimationType OffsetAnim { get; private set; }
[Serialize(0.0f, false)]
public float OffsetAnimSpeed { get; private set; }
private float rotationSpeedRadians;
[Serialize(0.0f, false)]
public float RotationSpeed
{
get
{
activeSprite = brokenSprite.Sprite;
break;
return MathHelper.ToDegrees(rotationSpeedRadians);
}
private set
{
rotationSpeedRadians = MathHelper.ToRadians(value);
}
}
return activeSprite;
[Serialize(0.0f, false)]
public float Rotation { get; private set; }
[Serialize(AnimationType.None, false)]
public AnimationType RotationAnim { get; private set; }
/// <summary>
/// If > 0, only one sprite of the same group is used (chosen randomly)
/// </summary>
[Serialize(0, false)]
public int RandomGroupID { get; private set; }
/// <summary>
/// The sprite is only drawn if these conditions are fulfilled
/// </summary>
public List<PropertyConditional> IsActiveConditionals { get; private set; } = new List<PropertyConditional>();
/// <summary>
/// The sprite is only animated if these conditions are fulfilled
/// </summary>
public List<PropertyConditional> AnimationConditionals { get; private set; } = new List<PropertyConditional>();
public DecorativeSprite(XElement element, string path = "")
{
Sprite = new Sprite(element, path);
SerializableProperty.DeserializeProperties(this, element);
foreach (XElement subElement in element.Elements())
{
List<PropertyConditional> conditionalList = null;
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "conditional":
case "isactiveconditional":
conditionalList = IsActiveConditionals;
break;
case "animationconditional":
conditionalList = AnimationConditionals;
break;
default:
continue;
}
foreach (XAttribute attribute in subElement.Attributes())
{
if (attribute.Name.ToString().ToLowerInvariant() == "targetitemcomponent") { continue; }
conditionalList.Add(new PropertyConditional(attribute));
}
}
}
public Vector2 GetOffset(ref float offsetState)
{
if (OffsetAnimSpeed <= 0.0f)
{
return Offset;
}
switch (OffsetAnim)
{
case AnimationType.Sine:
offsetState = offsetState % (MathHelper.TwoPi / OffsetAnimSpeed);
return Offset * (float)Math.Sin(offsetState * OffsetAnimSpeed);
case AnimationType.Noise:
offsetState = offsetState % (1.0f / (OffsetAnimSpeed * 0.1f));
float t = offsetState * 0.1f * OffsetAnimSpeed;
return new Vector2(
Offset.X * (PerlinNoise.GetPerlin(t, t) - 0.5f),
Offset.Y * (PerlinNoise.GetPerlin(t + 0.5f, t + 0.5f) - 0.5f));
default:
return Offset;
}
}
public float GetRotation(ref float rotationState)
{
if (rotationSpeedRadians <= 0.0f)
{
return Rotation;
}
switch (OffsetAnim)
{
case AnimationType.Sine:
rotationState = rotationState % (MathHelper.TwoPi / rotationSpeedRadians);
return Rotation * (float)Math.Sin(rotationState * rotationSpeedRadians);
case AnimationType.Noise:
rotationState = rotationState % (1.0f / rotationSpeedRadians);
return Rotation * PerlinNoise.GetPerlin(rotationState * rotationSpeedRadians, rotationState * rotationSpeedRadians);
default:
return rotationState * rotationSpeedRadians;
}
}
public void Remove()
{
Sprite?.Remove();
Sprite = null;
}
}
public List<BrokenItemSprite> BrokenSprites = new List<BrokenItemSprite>();
public List<DecorativeSprite> DecorativeSprites = new List<DecorativeSprite>();
public List<ContainedItemSprite> ContainedSprites = new List<ContainedItemSprite>();
public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
public Sprite InventoryIcon;
//only used to display correct color in the sub editor, item instances have their own property that can be edited on a per-item basis
[Serialize("1.0,1.0,1.0,1.0", false)]
public Color InventoryIconColor
{
get;
protected set;
}
public override void DrawPlacing(SpriteBatch spriteBatch, Camera cam)
@@ -50,12 +204,12 @@ namespace Barotrauma
if (!ResizeHorizontal && !ResizeVertical)
{
sprite.Draw(spriteBatch, new Vector2(position.X + sprite.size.X / 2.0f, -position.Y + sprite.size.Y / 2.0f), SpriteColor);
sprite.Draw(spriteBatch, new Vector2(position.X, -position.Y) + sprite.size / 2.0f * Scale, SpriteColor, scale: Scale);
}
else
{
Vector2 placeSize = size;
if (placePosition == Vector2.Zero)
{
if (PlayerInput.LeftButtonHeld()) placePosition = position;
@@ -73,5 +227,17 @@ namespace Barotrauma
if (sprite != null) sprite.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), placeSize, color: SpriteColor);
}
}
public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f)
{
if (!ResizeHorizontal && !ResizeVertical)
{
sprite.Draw(spriteBatch, new Vector2(placeRect.Center.X, -(placeRect.Y - placeRect.Height / 2)), SpriteColor, scale: Scale * scale);
}
else
{
if (sprite != null) sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), null, SpriteColor);
}
}
}
}