Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Singleplayer Crew Setup UI Refactor #14539

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using Microsoft.Xna.Framework;
using System.Linq;
using System;
using System.Xml.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Extensions;

namespace Barotrauma
{
Expand Down Expand Up @@ -129,4 +130,101 @@ public List<OutfitPreview> GetJobOutfitSprites(CharacterInfoPrefab charInfoPrefa
return outfitPreviews;
}
}

internal partial class JobVariant
{
private const int ItemsPerRow = 5;

private IEnumerable<JobPrefab.PreviewItem> PreviewItems => Prefab.PreviewItems[Variant].Where(it => it.ShowPreview);
private IEnumerable<Identifier> PreviewItemIdentifiers => PreviewItems.Select(it => it.ItemIdentifier).Distinct();

public GUIButton CreateButton(GUILayoutGroup parent, bool selected, GUIButton.OnClickedHandler onClicked)
{
GUIButton button = new GUIButton(new RectTransform(Vector2.One, parent.RectTransform, scaleBasis: ScaleBasis.BothHeight), (Variant + 1).ToString(), style: "JobVariantButton")
{
Selected = selected,
OnClicked = onClicked,
UserData = this
};

return button;
}

public GUIFrame CreateTooltip(GUIComponent parent, Point size, Point? position = null, Pivot? pivot = null, object data = null)
{
GUIFrame jobVariantTooltip = new GUIFrame(new RectTransform(size, GUI.Canvas, pivot: pivot), "GUIToolTip")
{
UserData = data
};

jobVariantTooltip.RectTransform.AbsoluteOffset = position.TryGetValue(out Point pos) ? pos : parent.Rect.Location;

GUILayoutGroup content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), jobVariantTooltip.RectTransform, Anchor.Center))
{
Stretch = true
};

new GUITextBlock(new RectTransform(Vector2.UnitX, content.RectTransform) { IsFixedSize = true }, TextManager.GetWithVariable("startingequipmentname", "[number]", (Variant + 1).ToString()), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center);

int rows = (int)Math.Max(Math.Ceiling(PreviewItemIdentifiers.Count() / (float)ItemsPerRow), 1);

new GUICustomComponent(new RectTransform(new Vector2(1f, 0.4f * rows), content.RectTransform, Anchor.BottomCenter), onDraw: DrawPreviewItems);

jobVariantTooltip.RectTransform.MinSize = new Point(0, content.RectTransform.Children.Sum(c => c.Rect.Height + content.AbsoluteSpacing));

return jobVariantTooltip;
}

private void DrawPreviewItems(SpriteBatch spriteBatch, GUIComponent parent)
{
Point slotSize = new Point(parent.Rect.Height);
int spacing = (int)(5 * GUI.Scale);
int slotCount = PreviewItemIdentifiers.Count();
int slotCountPerRow = Math.Min(slotCount, ItemsPerRow);
int rows = (int)Math.Max(Math.Ceiling(PreviewItemIdentifiers.Count() / (float)ItemsPerRow), 1);

float totalWidth = slotSize.X * slotCountPerRow + spacing * (slotCountPerRow - 1);
float totalHeight = slotSize.Y * rows + spacing * (rows - 1);
if (totalWidth > parent.Rect.Width)
{
slotSize = new Point(Math.Min((int)Math.Floor((slotSize.X - spacing) * (parent.Rect.Width / totalWidth)), (int)Math.Floor((slotSize.Y - spacing) * (parent.Rect.Height / totalHeight))));
}
int i = 0;
Rectangle tooltipRect = Rectangle.Empty;
LocalizedString tooltip = null;
foreach (Identifier itemIdentifier in PreviewItemIdentifiers)
{
if (MapEntityPrefab.FindByIdentifier(itemIdentifier) is not ItemPrefab itemPrefab) { continue; }

int row = (int)Math.Floor(i / (float)slotCountPerRow);
int slotsPerThisRow = Math.Min((slotCount - row * slotCountPerRow), slotCountPerRow);
Vector2 slotPos = new Vector2(parent.Rect.Center.X + (slotSize.X + spacing) * (i % slotCountPerRow - slotsPerThisRow * 0.5f), parent.Rect.Bottom - (rows * (slotSize.Y + spacing)) + (slotSize.Y + spacing) * row);

Rectangle slotRect = new Rectangle(slotPos.ToPoint(), slotSize);
Inventory.SlotSpriteSmall.Draw(spriteBatch, slotPos, scale: slotSize.X / (float)Inventory.SlotSpriteSmall.SourceRect.Width, color: slotRect.Contains(PlayerInput.MousePosition) ? Color.White : Color.White * 0.6f);

Sprite icon = itemPrefab.InventoryIcon ?? itemPrefab.Sprite;
float iconScale = Math.Min(Math.Min(slotSize.X / icon.size.X, slotSize.Y / icon.size.Y), 2f) * 0.9f;
icon.Draw(spriteBatch, slotPos + slotSize.ToVector2() * 0.5f, scale: iconScale);

int count = PreviewItems.Count(it => it.ItemIdentifier == itemIdentifier);
if (count > 1)
{
string itemCountText = "x" + count;
GUIStyle.Font.DrawString(spriteBatch, itemCountText, slotPos + slotSize.ToVector2() - GUIStyle.Font.MeasureString(itemCountText) - Vector2.UnitX * 5, Color.White);
}

if (slotRect.Contains(PlayerInput.MousePosition))
{
tooltipRect = slotRect;
tooltip = itemPrefab.Name + '\n' + itemPrefab.Description;
}
i++;
}
if (!tooltip.IsNullOrEmpty())
{
GUIComponent.DrawToolTip(spriteBatch, tooltip, tooltipRect);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,23 @@ sealed class SinglePlayerCampaignSetupUI : CampaignSetupUI
{
private GUIListBox subList;

protected GUILayoutGroup subPreviewContainer;
private GUILayoutGroup subPreviewContainer;

public CharacterInfo.AppearanceCustomizationMenu[] CharacterMenus { get; private set; }

private GUIButton nextButton;
private GUIListBox characterInfoColumns;

public SinglePlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer)
: base(newGameContainer, loadGameContainer)
{
CreateNewGameMenu();
}

private readonly Dictionary<GUIButton, GUIComponent> variantButtons = new Dictionary<GUIButton, GUIComponent>();

public SinglePlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer) : base(newGameContainer, loadGameContainer) => CreateNewGameMenu();

private int currentPage = 0;
private GUIListBox pageContainer;

public void Update()
{
float targetScroll =
(float)currentPage / ((float)pageContainer.Content.CountChildren - 1);
float targetScroll = (float)currentPage / (float)(pageContainer.Content.CountChildren - 1);

pageContainer.BarScroll = MathHelper.Lerp(pageContainer.BarScroll, targetScroll, 0.2f);
if (MathUtils.NearlyEqual(pageContainer.BarScroll, targetScroll, 0.001f))
Expand All @@ -47,6 +44,20 @@ public void Update()

pageContainer.HoverCursor = CursorState.Default;
pageContainer.Content.HoverCursor = CursorState.Default;

foreach ((GUIButton button, GUIComponent tooltip) in variantButtons)
{
Rectangle mouseRect = tooltip.MouseRect;
mouseRect.Inflate(16 * GUI.Scale, 16 * GUI.Scale);
tooltip.Visible = variantButtons.Keys.None(btn => btn != button && ButtonHovered(btn)) && (ButtonHovered(button) || tooltip.Visible && mouseRect.Contains(PlayerInput.MousePosition));
if (tooltip.Visible)
{
tooltip.RectTransform.AbsoluteOffset = (tooltip.UserData as GUIComponent).Rect.Location;
tooltip.AddToGUIUpdateList(order: 1);
}

static bool ButtonHovered(GUIButton button) => button.State is GUIComponent.ComponentState.Pressed or GUIComponent.ComponentState.Hover or GUIComponent.ComponentState.HoverSelected;
}
}

public void SetPage(int pageIndex)
Expand Down Expand Up @@ -293,24 +304,21 @@ public void RandomizeCrew()
}
characterInfos.Sort((a, b) => Math.Sign(b.Job.MinKarma - a.Job.MinKarma));

variantButtons.Clear();
characterInfoColumns.ClearChildren();
CharacterMenus?.ForEach(m => m.Dispose());
CharacterMenus = new CharacterInfo.AppearanceCustomizationMenu[characterInfos.Count];

for (int i = 0; i < characterInfos.Count; i++)
{
var subLayout = new GUILayoutGroup(new RectTransform(new Vector2(Math.Max(1.0f / characterInfos.Count, 0.33f), 1.0f),
characterInfoColumns.Content.RectTransform));

var (characterInfo, job) = characterInfos[i];

characterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.275f), subLayout.RectTransform));
GUILayoutGroup subLayout = new GUILayoutGroup(new RectTransform(new Vector2(Math.Max(1f / characterInfos.Count, 0.33f), 1f), characterInfoColumns.Content.RectTransform))
{
Stretch = true
};

var jobTextContainer =
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), subLayout.RectTransform), style: null);
var jobText = new GUITextBlock(new RectTransform(Vector2.One, jobTextContainer.RectTransform), job.Name, job.UIColor);
(CharacterInfo characterInfo, JobPrefab job) = characterInfos[i];

var characterName = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.1f), subLayout.RectTransform))
GUITextBox characterName = new GUITextBox(new RectTransform(new Vector2(1f, 0.1f), subLayout.RectTransform) { IsFixedSize = true })
{
Text = characterInfo.Name,
UserData = "random"
Expand All @@ -334,25 +342,64 @@ public void RandomizeCrew()
sender.Deselect();
return false;
};

var customizationFrame =
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f), subLayout.RectTransform), style: null);
CharacterMenus[i] =
new CharacterInfo.AppearanceCustomizationMenu(characterInfo, customizationFrame, hasIcon: false)

GUIFrame previewContainer = new GUIFrame(new RectTransform(new Vector2(1f, 0.25f), subLayout.RectTransform) { IsFixedSize = true }, "GUIFrameListBox");
GUIFrame previewContent = new GUIFrame(new RectTransform(previewContainer.Rect.Size - new Point(10, 5), previewContainer.RectTransform, Anchor.BottomCenter), null);

characterInfo.CreateIcon(new RectTransform(new Vector2(1f, 0.9f), previewContent.RectTransform, Anchor.Center));

GUITextBlock jobText = new GUITextBlock(new RectTransform(Vector2.UnitX, previewContent.RectTransform), job.Name, job.UIColor)
{
Padding = new Vector4(5f, 5f, 0f, 0f)
};

GUILayoutGroup variantGroup = new GUILayoutGroup(new RectTransform(new Point(previewContent.Rect.Width, (int)(32 * GUI.Scale)), previewContent.RectTransform, Anchor.BottomLeft), true)
{
AbsoluteSpacing = (int)(4 * GUI.Scale)
};
List<GUIButton> buttons = new List<GUIButton>();
for (int variant = 0; variant < job.Variants; variant++)
{
JobVariant jobVariant = new JobVariant(job, variant);

GUIButton button = jobVariant.CreateButton(variantGroup, characterInfo.Job.Variant == variant, (btn, data) =>
{
OnHeadSwitch = menu =>
{
if (characterName.UserData is string ud && ud == "random")
{
characterInfo.Name = characterInfo.GetRandomName(Rand.RandSync.Unsynced);
characterName.Text = characterInfo.Name;
characterName.UserData = "random";
}
buttons.ForEach(button => button.Selected = false);
btn.Selected = true;
characterInfo.Job.Variant = (data as JobVariant).Variant;
return true;
});

StealRandomizeButton(menu, jobTextContainer);
buttons.Add(button);
variantButtons.Add(button, jobVariant.CreateTooltip(subLayout, previewContent.Rect.Size + new Point(0, jobText.Rect.Height - variantGroup.Rect.Height), data: subLayout));
}

GUIFrame customizationFrame = new GUIFrame(new RectTransform(Vector2.One, subLayout.RectTransform), style: null);
CharacterMenus[i] = new CharacterInfo.AppearanceCustomizationMenu(characterInfo, customizationFrame, hasIcon: false)
{
OnHeadSwitch = (menu) =>
{
menu.CharacterInfo.Job.Variant = Rand.Range(0, characterInfo.Job.Prefab.Variants);
buttons.ForEach(button => button.Selected = false);
buttons.First(button => (button.UserData as JobVariant).Variant == characterInfo.Job.Variant).Selected = true;

if (characterName.UserData is "random")
{
characterInfo.Name = characterInfo.GetRandomName(Rand.RandSync.Unsynced);
characterName.Text = characterInfo.Name;
characterName.UserData = "random";
}
};
StealRandomizeButton(CharacterMenus[i], jobTextContainer);

menu.RandomizeButton.RectTransform.Parent = null;
}
};

GUIButton randomizeButton = new GUIButton(new RectTransform(new Point((int)(32 * GUI.Scale)), previewContent.RectTransform, Anchor.BottomRight) { AbsoluteOffset = new Point(0, (int)(5 * GUI.Scale)) }, style: "RandomizeButton")
{
OnClicked = CharacterMenus[i].RandomizeButton.OnClicked
};

CharacterMenus[i].RandomizeButton.RectTransform.Parent = null;
}
}

Expand All @@ -373,16 +420,6 @@ private void CreateCustomizeWindow(CampaignSettings prevSettings, Action<Campaig
};
}

private static void StealRandomizeButton(CharacterInfo.AppearanceCustomizationMenu menu, GUIComponent parent)
{
//This is just stupid
var randomizeButton = menu.RandomizeButton;
var oldButton = parent.GetChild<GUIButton>();
parent.RemoveChild(oldButton);
randomizeButton.RectTransform.Parent = parent.RectTransform;
randomizeButton.RectTransform.RelativeSize = Vector2.One * 1.3f;
}

private bool FinishSetup(GUIButton btn, object userdata)
{
if (string.IsNullOrWhiteSpace(saveNameBox.Text))
Expand Down
Loading